variable is not defined in cypress - cypress

I am using this code to make random selection. But in the end of this code I want to save the variable to use further in the test-cases. I did so, but I got the error
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item') // we get the select/option by finding the select by class
.then(listing => {
const randomNumber = getRandomInt(0, listing.length-1); //generate a rendom number between 0 and length-1. In this case 0,1,2
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item').eq(randomNumber).then(($select) => { //choose an option randomly
const text = $select.text() //get the option's text. For ex. "A"
cy.get('div.cdk-virtual-scroll-content-wrapper').contains(text).click() // select the option on UI
let region = text;
cy.wrap(region).as('region')
});
})
cy.log(region)

You can do this in case you want to use the value of the region within the same test (same it block).
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item') // we get the select/option by finding the select by class
.then((listing) => {
const randomNumber = getRandomInt(0, listing.length - 1) //generate a rendom number between 0 and length-1. In this case 0,1,2
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item')
.eq(randomNumber)
.then(($select) => {
//choose an option randomly
const text = $select.text() //get the option's text. For ex. "A"
cy.get('div.cdk-virtual-scroll-content-wrapper').contains(text).click() // select the option on UI
let region = text
cy.wrap(region).as('region')
})
})
cy.get('#region').then((region) => {
cy.get('selector').type(region)
})
In case if you use the value of text, in different test(different it block) or even different test suite you can do this:
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item') // we get the select/option by finding the select by class
.then((listing) => {
const randomNumber = getRandomInt(0, listing.length - 1) //generate a rendom number between 0 and length-1. In this case 0,1,2
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item')
.eq(randomNumber)
.then(($select) => {
//choose an option randomly
const text = $select.text() //get the option's text. For ex. "A"
cy.get('div.cdk-virtual-scroll-content-wrapper').contains(text).click() // select the option on UI
let region = text
Cypress.env('region', region)
})
})
cy.get('selector').type(Cypress.env('region'))

Add .then() to your log, and move the declaration of region to the top.
let region;
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item')
.then(listing => {
const randomNumber = getRandomInt(0, listing.length-1); //generate a rendom number between 0 and length-1. In this case 0,1,2
cy.get('div.cdk-virtual-scroll-content-wrapper nz-option-item').eq(randomNumber).then(($select) => { //choose an option randomly
const text = $select.text() //get the option's text. For ex. "A"
cy.get('div.cdk-virtual-scroll-content-wrapper').contains(text).click() // select the option on UI
region = text;
cy.wrap(region).as('region')
});
})
cy.then(() => {
cy.log(region)
})

Related

How to go page by page in cypress?

I have ajax table in my web and this table has many pages. My goal is to go through all the rows and columns, go to the next page and make the following procedure until the end of the page. I have the following code:
for(let i = 0; i < 10; i++){
cy.get('tr:has(td)').each(($tr, rowIndex) => { // look at each row individually
cy.wrap($tr)
.find('td') // all cols inside this row
.invoke('slice', 5, 8) // filter to last 3 cols only
.invoke('text') // get all the text in one lump
.then(text => {
if (text.trim() === '') { // after trim(), is all text empty?
console.log(`Row ${rowIndex+1} has three empty cols`)
cy.wrap($tr).find('td').eq(5).click({force: true}) // click the 6th col
cy.wait(4000)
cy.get('.review-modal_textarea_3L5AQ').eq(0).should('be.visible').type(str)
cy.get('.pagination_pagination_2iOOV > :nth-child(4)').click({force: true}) //clicking to the next page
}
I managed to go through all rows and columns in one page, but having problem going to the next page (its going just one time). I think I have problem with loop. It simply doesn't working. I have i<10 because I have 10 pages, but the pages can be more (may there is a way to do it without for loop). How can I solve this problem?
I think you can do it by wrapping the per-page code in a function and calling it at the right time.
The trouble with for(let i = 0; i < 10; i++){ it's not letting the page change complete.
function handlePage() { // naf function name, but for illustration
cy.get('tr').each(($tr, rowIndex) => { // look at each row individually
cy.wrap($tr)
.find('td') // all cols inside this row
.invoke('slice', 5, 8) // filter to last 3 cols only
.invoke('text') // get all the text in one lump
.then(text => {
if (text.trim() === '') { // after trim(), is all text empty?
console.log(`Row ${rowIndex+1} has three empty cols`)
cy.wrap($tr).find('td').eq(5).click() // click the 6th col
cy.wait(4000)
cy.get('.review-modal_textarea_3L5AQ').eq(0).should('be.visible').type(str)
}
})
})
}
cy.visit(...) // whatever gets 1st page
handlePage() // 1st page
Cypress._.times(9, () => { // 9 more pages
cy.get('.pagination_pagination_2iOOV > :nth-child(4)').click({force: true})
.then(() => { // after page transition
// may need a cy.wait() or test page number on screen
handlePage()
})
})

dc.js charting nested data arrays

Problem description
I have a large data array with a structure similar to the following and seek to create a chart that will display the timerecords' changesets by hour that the changeset was created.
[ // array of records
{
id: 1,
name: 'some record',
in_datetime: '2019-10-24T08:15:00.000000',
out_datetime: '2019-10-24T10:15:00.000000',
hours: 2,
tasks: ["some task name", "another task"],
changesets: [ //array of changesets within a record
{
id: 1001,
created_at: '2019-10-24T09:37:00.000000'
},
...
]
},
...
]
No matter how I have tried to create the dimension/write reduction functions I can't get the correct values out of the data table.
const changeReduceAdd = (p, v) => {
v.changesets.forEach(change => {
let cHour = timeBand[change.created_hour]
if (showByChanges) {
p[cHour] = (p[cHour] || 0) + (change.num_changes || 0)
} else {
p[cHour] = (p[cHour] || 0) + 1 //this is 1 changeset
}
})
return p
}
const changeReduceRemove = (p, v) => {
v.changesets.forEach(change => {
let cHour = timeBand[change.created_hour]
if (showByChanges) {
p[cHour] = (p[cHour] || 0) - (change.num_changes || 0)
} else {
p[cHour] = (p[cHour] || 0) - 1 //this is 1 changeset
}
})
return p
}
const changeReduceInit = () => {
return {}
}
//next create the array dimension of changesets by hour
//goal: show changesets or num_changes grouped by their created_hour
let changeDim = ndx.dimension(r => r.changesets.map(c => timeBand[c.created_hour]), true)
let changeGroup = changeDim.group().reduce(changeReduceAdd, changeReduceRemove, changeReduceInit)
let changeChart = dc.barChart('#changeset-hour-chart')
.dimension(changeDim)
.keyAccessor(d => d.key)
.valueAccessor(d => d.value[d.key])
.group(changeGroup)
jsfiddle and debugging notes
The main problem I'm having is I want the changesets/created_hour chart, but in every dimension I have tried, where the keys appear correct, the values are significantly higher than the expected.
The values in the "8AM" category give value 5, when there are really only 3 changesets which I marked created_hour: 8:
There are a lot of solutions to the "tag dimension" problem, and you happen to have chosen two of the best.
Either
the custom reduction, or
the array/tag flag parameter to the dimension constructor
would do the trick.
Combining the two techniques is what got you into trouble. I didn't try to figure what exactly was going on, but you were somehow summing the counts of the hours.
Simple solution: use the built-in tag dimension feature
Use the tag/dimension flag and default reduceCount:
let changeDim = ndx.dimension(r => r.changesets.map(c => timeBand[c.created_hour]), true)
let changeGroup = changeDim.group(); // no reduce, defaults to reduceCount
let changeChart = dc.barChart('#changeset-hour-chart')
.dimension(changeDim)
.keyAccessor(d => d.key)
.valueAccessor(d => d.value) // no [d.key], value is count
.group(changeGroup)
fork of your fiddle
Manual, pre-1.4 groupAll version
You also have a groupAll solution in your code. This solution was necessary before array/tag dimensions were introduced in crossfilter 1.4.
Out of curiosity, I tried enabling it, and it also works once you transform from the groupAll result into group results:
function groupall_map_to_group(groupall) {
return {
all: () => Object.entries(groupall.value())
.map(([key,value])=>({key,value}))
}
}
let changeGroup = ndx.groupAll().reduce(changeReduceAdd, changeReduceRemove, changeReduceInit)
let changeChart = dc.barChart('#changeset-hour-chart')
.dimension({}) // filtering not implemented
.keyAccessor(d => d.key)
.valueAccessor(d => d.value) // [d.key]
.group(groupall_map_to_group(changeGroup))
.x(dc.d3.scaleBand().domain(timeBand))
.xUnits(dc.units.ordinal)
.elasticY(true)
.render()
crossfilter 1.3 version

Updating react-table values after Dragging and Dropping a row in React Redux

I´ve accomplished the react drag and drop functionality into my project so i can reorder a row in a react table´s list. The problem is i have a column named 'Sequence', witch shows me the order of the elements, that i can´t update its values.
Example:
before (the rows are draggable):
Sequence | Name
1 Jack
2 Angel
after ( i need to update the values of Sequence wherea i change their position after dropping a specific draggable row, in this case i dragged Jack at the first position and dropped it at the second position) :
Sequence | Name
1 Angel
2 Jack
React/Redux it´s allowing me to change the index order of this array of elements, without getting the 'A state mutation was detected between dispatches' error message, but is not allowing me to update the Sequence values with a new order values.
This is what i have tried so far:
// within the parent class component
// item is an array of objects from child
UpdateSequence(startIndex, endIndex, item) {
// the state.Data is already an array of object
const result = this.state.Data;
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
// this is working without the mutation state error
this.setState({ Data: result })
let positionDiff = 0;
let direction = null;
let newIndex = 0;
positionDiff = endIndex - startIndex;
if (startIndex > endIndex) {
direction = "up";
}
else if (startIndex < endIndex) {
direction = "down";
}
if (positionDiff !== 0) {
for (var x = 0; x <= Math.abs(positionDiff); x++) {
if (x === 0) {
newIndex = startIndex + positionDiff - x;
this.setState(prevState => ({
Data: {
...prevState.Data,
[prevState.Data[newIndex].Sequence]: Data[newIndex].Sequence + positionDiff
},
}));
}
else {
if (direction === "down") {
newIndex = startIndex + positionDiff - x;
this.setState(prevState => ({
Data: {
...prevState.Data,
[prevState.Data[newIndex].Sequence]: Data[newIndex].Sequence - 1
},
}));
}
else if (direction === "up") {
Data= startIndex + positionDiff + x;
this.setState(prevState => ({
Data: {
...prevState.Data,
[prevState.Data[newIndex].Sequence]: Data[newIndex].Sequence + 1
},
}));
}
}
}
// so when i call save action i am stepping into the 'A state mutation was detected between dispatches' error message.
this.props.actions.saveSequence(this.state.Data)
.then(() => {
this.props.actions.loadData();
})
.catch(error => {
toastr['error'](error, 'error....');
})
}
Calling the action 'saveSequence' whenever i try to update the element of the array, 'Sequence', i am getting the 'A state mutation was detected between dispatches' error message.
Any help will be greatfull! Thank you!
note: The logic applied to reorder the Sequence is ok.
While I don't know redux particularly well, I am noticing that you are directly modifying state, which seems like a likely culprit.
const result = this.state.Data;
const [removed] = result.splice(startIndex, 1);
splice is a destructive method that modifies its input, and its input is a reference to something in this.state.
To demonstrate:
> state = {Data: [1,2,3]}
{ Data: [ 1, 2, 3 ] }
> result = state.Data.splice(0,1)
[ 1 ]
> state
{ Data: [ 2, 3 ] }
Notice that state has been modified. This might be what Redux is detecting, and a general React no-no.
To avoid modifying state, the easy way out is to clone the data you are looking to modify
const result = this.state.Data.slice()
Note that this does a shallow copy, so if Data has non-primitive values, you have to watch out for doing destructive edits on those values too. (Look up deep vs shallow copy if you want to find out more.) However, since you are only reordering things, I believe you're safe.
Well, i figured it out changing this part of code:
//code....
const result = item;
const [removed] = result.splice(startIndex, 1);
// i created a new empty copy of the const 'removed', called 'copy' and update the Sequence property of the array like this below. (this code with the sequence number is just a sample of what i came up to fix it )
let copy;
copy = {
...removed,
Sequence: 1000,
};
result.splice(endIndex, 0, copy);
After i didn´t setState for it, so i commented this line:
// this.setState({ Data: result })
//...code
and the end of it was putting the result to the save action as a parameter , and not the state.
this.props.actions.saveSequence(result)
Works and now i have i fully drag and drop functionality saving the new order sequence into the database with no more 'A state mutation was detected between dispatches' error message!

How can i compare arrays in Cypress?

I want to compare data present in an array with the data retrieved from a class using .each in cypress?
Using below code i have tried to iterate over the FileType array using below code.
const Filetype = ['Access', 'Excel', 'Text/CSV','PDF','JSON','dsdsd'];
const te = cy.wrap($bodyFind).find('.data-sourcename').should('have.length',6).each(($li) => { cy.log($li.text()); });
te.each(($text)=> {
cy.log("Te" + $text);
//prints ['Access','Excel','Text/CSV','PDF','JSON','XML'];
});
// Converted FileType Array to Cypress object using cy.wrap command.
const cywrap = cy.wrap(Filetype);
te.each((e1)=>cywrap.each((e2)=> {
if(e1 == e2) {
expect(e1).equals(e2);
}
}));
But the value of e1 and e2 is same.
expect should fail with 'dsdsd' is equals 'XML'
whereas it passes with 'dsdsd' is equals 'dsdsd'
You could use map here.
const filetypes = ['Access', 'Excel', 'Text/CSV','PDF','JSON','dsdsd'];
cy.get('.data-sourcename').should(($els) => {
// map jquery elements to array of their innerText
const elsText = $els.toArray().map(el => el.innerText)
expect(elsText).to.deep.eq(filetypes)
})
I hope by now you must have found out the solution for this. But still, there was no selected answers yet, thought I would add one.
const Filetype = ['Access', 'Excel', 'Text/CSV','PDF','JSON','dsdsd'];
cy
.get('whatever element')
.each(($span, i) => {
expect($span.text()).to.equal(Filetype[i]);
});
$span would go through each element and .text()will get the text value of that element.

set an id at "Raphaeljs set"

i want to set an id to Raphael set because i want to display it after an event click.
this is my set:
var divResult = document.getElementById('printResult');
var space2Draw = Raphael(divResult, 1600, 900);
var st = space2Draw.set();
st.push(
space2Draw.circle(newXCoordinates, newYCoordinates, 20).click((function (valore) {
return function () {
window.open("index-point.html?id=" + (valore) + "&type=" + type + "&description=" + description + "&name=" + name);
}
}(valore))).mouseover(function () {
this.attr({ 'cursor': 'pointer' });
this.attr({ 'opacity': '.50' });
}).mouseout(function () {
this.attr({ 'opacity': '1' });
})
);
in my page i have a button:
function show(){
var element = space2Draw.getById(-1);
element.show();
}
}
Is not possible to set an id in this way : set.id = -1?
How can I set an id and then I find the set?
Thanks in advance for the help.
You can try using setAttribute() to add a CLASS for the elements you want to access later, and then modify their CSS propperties. The first step would be to change the CLASS of each element:
myElement.node.setAttribute("class", "class_name");
Unfortunately, Raphael does not allow you to handle sets as unique HTML objects, so you cannot do this for the entire set at once. Instead, you might have to do this for each element in your set, possibly with a for cycle, something like this:
for (var i=0; i<st.length; i++) {
st[i].node.setAttribute("class", "class_name");
}
Then, using JQuery, you can modify the CSS properties of the CLASS you created in order to display the elements in your set.
function show(){
$('.class_name').css('display', 'block');
}
I hope this helps.
Maybe you can use Raphael data() function to assign any data to your element/set.
Example:
// you can keep global var for your id and increment it within your code
var id = 0;
var p = Raphael(100, 100, 500, 500),
r = p.rect(150, 150, 80, 40, 5).attr({fill: 'red'}),
c = p.circle(200, 200, 70).attr({fill: 'blue'});
var set = p.set(r,c).data("id", id);
id++;
// then in your click event to get the id you can do
var whichSet = this.data("id");
// data("id") will return you the global id variable
Good Luck

Resources