How to adding values from dynamic elements and verifying the result - javascript

Similarly to this question. How add two values[1,2 elements] and verify its equal to result[3 element]
from the below HTML structure I need to add A(element) & B(element) and verify the sum is equal to A+B(element) its complex for me since A & B are dynamic and they can or cannot be present depending on the changes from the user.
I tried to use the below script.
cy.get('[data-cy="Selected in Final InterviewofferBreakUpTableBodyCellRenderer"] >span').invoke('text')
But it's yielding both the text, i only need the number.
I need help in making a test that won't fail even if only one element A or B is present.

You can do something like this:
cy.contains('span', 'Final Interview Selects')
.next()
.invoke('text')
.then((sum) => {
cy.contains('span', 'Selected - Not Offered')
.next()
.invoke('text')
.then((A) => {
cy.contains('span', 'Selected in Final Interview')
.next()
.invoke('text')
.then((B) => {
expect(+sum).to.eq(+A + +B)
})
})
})
You can do something like this if some of the elements are not visible.
const A = 0,
B = 0
cy.get('body')
.then(($body) => {
if (
'$body:not(:contains("Selected - Not Offered"))' &&
'$body:contains("Selected in Final Interview")'
) {
A = 0
cy.contains('span', 'Selected in Final Interview')
.next()
.invoke('text')
.then((num) => {
B = +num
})
}
if (
'$body:contains("Selected - Not Offered")' &&
'$body:not(:contains("Selected in Final Interview"))'
) {
B = 0
cy.contains('span', 'Selected - Not Offered')
.next()
.invoke('text')
.then((num) => {
A = +num
})
}
if (
'$body:not(:contains("Selected - Not Offered"))' &&
'$body:not(:contains("Selected in Final Interview"))'
) {
A = 0
B = 0
}
})
.then(() => {
cy.contains('span', 'Final Interview Selects')
.next()
.invoke('text')
.then((sum) => {
expect(+sum).to.eq(A + B)
})
})

Related

How to list points from highest to lowest?

this code displays the points list randomly ,I want to display points list from the highest to the lowest.
if (command == prefix + 'points') {
if (!message.guild.member(client.user).hasPermission('EMBED_LINKS'))
return message.channel.send(':no_entry: | I dont have Embed Links permission.');
if (!args[1]) {
if (!points)
return message.channel.send(embed);
var members = Object.values(points, null, 5);
var memb = members.filter(m => m.points >= 1);
if (memb.length == 0)
return message.channel.send(embed);
var x = 1;
let pointsTop = new Discord.MessageEmbed()
.setAuthor('Points:')
.setColor('#79758F')
.setDescription(
memb.sort(
(second, first) => first.points > second.points
)
.slice(0, 10)
.map(m => `<#${m.id}> \`${m.points}\``).join('\n')
)
message.channel.send({ embed: pointsTop });
You can use - in Array#sort()
.setDescription(memb.sort((a, b) => a.points - b.points).slice(0, 10).map(m => `${m} \`${m.points}\``).join('\n');
Sort functions should return numbers, not booleans
This is the only part of your code sample that matters:
memb.sort(
(second, first) => first.points > second.points
)
Your custom sort function returns a boolean. That's not how Array.prototype.sort works.
return a negative number if the first argument should sort before the second argument
return a positive number if the second argument should sort before the first argument
return 0 if neither belongs before the other
You probably want this:
memb.sort(
(first, second) => second.points - first.points
)

how to filter strings in nested array?

I'm trying to implement a search function that returns find which has search word in the specified array. Let's say, a collection has [aa, ab, aaa], and search word is "a". In this case, return the object to display. Because at least one of the strings in the array has 'a'.
dataStructure
[
{
name:'aa',
searchWords:['aa','ab','bc'] <- I want to use this array for search
},
{
name:'bb',
searchWords:['bb','bc','de'] <- I want to use this array for search
},
...
]
I tried to fix the issue, by using includes(), filter(),indexOf(). However still it returns nothing or returns data when the search word is exactly matched.
How to fix the code to achieve aiming?
Thank you in advance!
this part works well
let filterStudents = students;
if (searchName.length > 0 && searchTag.length === 0) {
filterStudents = students.filter((student) => {
if (
student.firstName.toLowerCase().includes(searchName.toLowerCase())
|| student.lastName.toLowerCase().includes(searchName.toLowerCase())
) {
return true;
}
return false;
});
Problem happens on this part
} else if (searchName.length === 0 && searchTag.length > 0) {
filterStudents = students.filter(
(student) => {
console.log(student.tags);
student.tags.filter((tag) => {
console.log(tag);
tag.indexOf(searchTag) > -1;
});
},
);
} else if (searchName.length > 0 && searchTag.length > 0) {
} else {
console.log('both');
}
You don't return from the filter callbacks
As a sidenote, there is also String#includes:
filterStudents = students.filter(student =>
student.tags.some((tag) => tag.includes(searchTag))
);
If you only want to search for matches when the corresponding searchTag or searchName is filled out, use the conditional operator inside the filter callback to check whether the filter test should be carried out:
const students = [
{
name:'aa',
searchWords:['aa','ab','bc']
},
{
name:'bb',
searchWords:['bb','bc','de']
},
];
const doFilter = () => {
const [searchName, searchTag] = [...document.querySelectorAll('input')]
.map(input => input.value.toLowerCase());
const filtered = students.filter(({ name, searchWords }) => (
(searchName ? name.toLowerCase().includes(searchName) : true) &&
(searchTag ? searchWords.some(word => word.toLowerCase().includes(searchTag)) : true)
));
code.textContent = JSON.stringify(filtered);
};
window.addEventListener('change', doFilter);
<input placeholder="searchName">
<input placeholder="searchTag">
<div>
<code id="code"></code>
</div>

RxJS Debounce with priority

I'm having trouble coming up with this stream.
What I'm looking for is something like debounceTime but with priority.
So if I have events with the shape { type: 'a', priority: 2 }. These events needs to be debounced by a few seconds but instead of the last event being emitted, the event with the highest priority is emitted.
input stream:
------(a|1)--(b|3)---(c|2)-----------------------(a|1)-----------------
output stream:
-----------------------------------(b|3)---------------------(a|1)-----
I've try looking at other operators like window and filtering through the result for the last event but it's not ideal because window work on a fixed cadence where I want the timer to start on the first event like debouncing does.
You have to store and update the item with the highest priority and map to this highest value which you then pass to debounceTime.
let highest = null;
source$.pipe(
map(v => highest = highest && highest.priority > v.priority ? highest : v),
debounceTime(2000),
tap(() => highest = null)
);
You can create your own operator that does this with the help of defer. defer makes sure that every subscriber gets its own highest variable, as every subscriber will get its own new Observable created by calling the given factory function.
function debounceTimeHighest<T>(dueTime: number, getHighest: (curr: T, high: T) => T): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) => defer(() => {
let highest: T = null;
return source.pipe(
map(item => highest = highest ? getHighest(item, highest) : item),
debounceTime(dueTime),
tap(() => highest = null)
);
});
}
// Usage
source$.pipe(
debounceTimeHighest(2000, (v1, v2) => v1.priority >= v2.priority ? v1 : v2)
)
The code above is Typescript. If you want plain Javascript just remove all the types.
https://stackblitz.com/edit/rxjs-hitqxk
I'll offer the following solution, based around using scan to offer up the highest given priority emission so far for consideration by debounceTime(). Note that scan needs to reconsider new data after every successful debounce, so I use the operator window() to split up the emissions, starting a new observable window after every emission by debounceTime().
Here is the CodeSandbox
And here is some simplified code from the CodeSandbox showing the important bits:
const resetScan$ = new Subject();
source$.pipe(
window(resetScan$),
mergeMap(win$ => win$.pipe(
scan((acc, cur) => acc.priority >= cur.priority ? acc : cur )
)),
debounceTime(debounceDelay),
tap(() => resetScan$.next())
);
You can combine the debounceTime and buffer and filter operator to achieve what you need. I have developed this small example for it.
https://stackblitz.com/edit/typescript-lwzt4k
/*
Collect clicks that occur, after 250ms emit array of clicks
*/
clicks$.pipe(
buffer(clicks$.pipe(debounceTime(1000))),
// if array is greater than 1, double click occured
map((clickArray) => {
document.querySelector('#emittedObjects').innerHTML = (`<div>${JSON.stringify(clickArray)}</div>`);
const sortedArray = clickArray.sort((a, b) => {
return a.priority < b.priority ? 1 : -1;
});
const output = sortedArray.length > 0 ? sortedArray[0] : null;
document.querySelector('#mappedOutput').innerHTML = JSON.stringify(output);
return output;
})
)
.subscribe((obj) => {
const str = obj ? JSON.stringify(obj) : 'NULL';
document.querySelector('#throttledOutput').innerHTML = `<div>THROTTLED: ${str}</div>`;
});

Assertion on sorting a table in cypress

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

Javascript - array.map match index

There's an array looking as follows:
[[3,0], [6,0], [2,0], [9,0]....]
I'm trying to create a React/Redux reducer that changes the value of one of the 0s to 1. I click on an element and an action is dispatched. Idx is the index of an element in an array (eg. 0, 1, 2, 3)
export const toggleTile = (idx) => {
return {
type: TOGGLE_TILE,
idx
};
};
The reducer below does not work as I'd like it to be. I just created the skeleton of the conditional statements. If I click on a tile with index 3 (so the fourth tile), it changes the [n,0] to [n,1] for all elements. First of all it should only do if I click any of the tiles, and it should change [n,0] to [n,1] only for the clicked tile so I'm trying to change the 3 in the code below to the index of an 'i' element being mapped.
export default (state = [], action = {}) => {
switch (action.type) {
case LOAD_GRID:
return action.payload || [];
case TOGGLE_TILE:
return state.map((i) => {
if (action.idx === 3) {
return (i[1] === 0
? [i[0], parseInt(i[1], 10) + 1]
: [i[0], parseInt(i[1], 10) - 1]
);
}
return [i[0], i[1]];
});
default:
return state;
}
};
A grid component:
export default class Grid extends Component {
render() {
const mygrid = [];
this.props.inGrid.forEach((r, i) => {
mygrid.push(
<Square
key={i}
idx={i}
sqValue={r}
toggleTile={this.props.toggleTile}
/>
);
});
const { grid } = styles;
return (
<View style={grid}>
{mygrid}
</View>
);
}
}
export default class Square extends Component {
myaction() {
this.props.toggleTile(this.props.idx);
console.log(this.props.idx);
}
render() {
const { square, textStyle, squareActive } = styles;
const { sqValue } = this.props;
return (
<TouchableHighlight
style={[square, sqValue[1] && squareActive]}
onPress={this.myaction.bind(this)}
>
<View>
<Text style={textStyle}>{sqValue[0]},{sqValue[1]}</Text>
</View>
</TouchableHighlight>
);
}
}
Please advise.
There are a number of ways you can do this, with varying degrees of verbosity (due to Redux's insistence on immutability), but here's a pretty straightforward one:
case TOGGLE_TILE:
const nextValue = state[action.idx].slice(); // Make a copy of the tuple to be toggled
nextValue[1] = nextValue[1] === 0 ? 1 : 0; // Toggle it
const nextState = state.slice(); // Make a copy of the state
nextState[action.idx] = nextValue; // Replace the old tuple with the toggled copy
return nextState;
Or:
case TOGGLE_TILE:
const prevValue = state[action.idx];
const nextState = state.slice();
nextState[action.idx] = [ prevValue[0], prevValue[1] === 0 ? 1 : 0 ];
return nextState;
Ok, I'm gonna try and see what we can do with just the following portion of code you shared.
I would like to note that the code presented is not succinct. It would be a great benefit to yourself, your team, as well as anyone here on this site if your code was refactored the more you understand what you need to build.
// So state is just an array of arrays...
var state = [3,0], [6,0], [2,0], [9,0]];
return state.map((i) => { // i => [3,0] or [9,0] !! i is not index !!
// Map is going to iterate over the entire array of arrays.
if (action.idx === 3) {
// action.idx is what comes in from the click.
// Here is where your doing your work.
// If the first element of "i" is zero, then
// return the same array but add 1 to the second element of array.
// so [3,0] or [4,0] should become [3,1] or [4,1] but only for #3 as
// action.idx === 3 says to only change when... Nope, this is not how it will work. You need your exception in the MAP.
return (i[1] === 0 ? [i[0], parseInt(i[1], 10) + 1] : [i[0], parseInt(i[1], 10) - 1]);
}
// ?? Why don't you just return i, as i is each array of numbers.
return [i[0], i[1]];
});
// It seams to me that something like this should work, just plug and play.
// I am assuming a few things here that I will spell out. If they are incorrect, let me know and I'll change it.
// state will be an array of arrays that only contain two numbers each.
// They may or may not be in original order.
// The second element of each array will be either 0 or 1.
var state = [3,0], [6,0], [2,0], [9,0]];
state.map(function(cell){ // call it what you want, you called it "i".
if(cell[0] === action.idx){ // If clicked action index is === to cell[0]
// You could just hard code 3 as you did above, but this makes it dynamic.
// and only changes the cell that was clicked.
cell[1] = cell[1] ? 1 : 0; // if cell[1] is 0, then it is falsey, no need for complex logic. No need to parseInt if they are numbers to begin with. But if you do, then use "+" to change a string to number.
}
return cell;
});
Without notes
var state = [3,0], [6,0], [2,0], [9,0]];
state.map(function(cell){
if(cell[0] === action.idx){
cell[1] = cell[1] ? 1 : 0;
}
return cell;
});

Categories