I would like to know if Cypress supports nested loops?
In my case, I have a table where I need to iterate over each row (row is represented by the "data-automationid="DetailsRowFields"") and then over each cell.
Although I attempted to create a function that would extract it, I am unsure if it can be done.
export function getCellData() : Promise<Map<string, string>> {
return new Cypress.Promise((resolve) => {
const cellMap = new Map<string, string>();
cy.get('div[data-automationid="DetailsRowFields"]')
.each((rowElement) => {
rowElement.children()
.each((child) => {
const ariaIndex = child.attr('aria-colindex');
const cellData = child.text();
cellMap.set(ariaIndex, cellData);
});
})
.then(() => resolve(cellMap));
});
}
That won't quite work as you expect, you will only get the last row data.
The Map key should include the row index.
Also, rowElement is a jQuery object. While jQuery does have methods for .children() and .each(), the calling pattern is different to the equivalent Cypress .each().
I recommend wrapping the rowElement.
const cellMap = new Map<string, string>();
cy.get('div[data-automationid="DetailsRowFields"]')
.each((rowElement, rowIndex) => {
cy.wrap(rowElement)
.children()
.each((child) => {
const ariaIndex = child.attr('aria-colindex');
const cellData = child.text();
const mapKey = `${rowIndex}:${ariaIndex}`
cellMap.set(mapKey, cellData);
})
})
Related
I have a complicated selection that I've put into a function to keep the test clean. I want to call the function before and after some page actions and compare the results.
This is my code, problem is I'm not getting the result back even though the value is extracted successfully inside the function.
const getVals = () => {
// simplified
cy.get('[id="22"] span')
.then($els => {
const vals = [...$els].map(el => el.innerText)
return vals
})
}
const vals1 = getVals()
// perform action on the page
const vals2 = getVals()
// compare
expect(vals1).to.deep.eq(vals2)
The function has a return inside .then() but it's not returning the result from the main body of the function.
Since the commands are asynchronous, I recommend changing to a custom command and adding alias to preserve the result during the intermediate actions
Cypress.Commands.add('getVals', () => {
cy.get('[id="22"] span')
.then($els => {
const vals = [...$els].map(el => el.innerText)
return vals // vals is Cypress "subject"
})
}
cy.getVals().as('vals1')
// perform action on the page
cy.getVals().then(vals2 => { // use then to obtain vals
cy.get('#vals1').then(vals1 => { // retrieve first from alias
// compare
expect(vals1).to.deep.eq(vals2)
})
})
I have the next code, and it was working properly. to execute a request to my method fetchDropdownDataByFederationId, but now I have a requirement to execute the same method x number of times.
fetchInProgress(queryString?): Observable<IPerson[]> {
let PersonList: IPerson[] = [];
return this.getItems<IPerson[]>('', queryString).pipe(
take(1),
switchMap((wls: IPerson[]) => {
PersonList = [...wls];
//const createdbyIds = [...new Set(wls.map((f) => f.createdBy))];
return this.teamPageService.getInformation(wls.createdBy);
}),
map((teams:any) => {
console.log('> teams', teams);
for (let i = 0; i < PersonList.length; i++) {
//update information
}
//console.log('> Final value: ', PersonList);
return PersonList;
})
);
}
But, I'm not finding a way to execute my SwitchMap x number of times and get the results back to use them in my map method to parse the information.
I just moved my SwitchMap to mergeMap, something like this:
mergeMap((wls: IWalklist[]) => {
//let allIds = wls.contact.map(id => this.getSingleData(id._id) );
let drops: Dropdown[] = [];
walklistList = [...wls];
const allIds = [...new Set(wls.map((f) => f.createdBy))];
return forkJoin(...allIds).pipe(
map((idDataArray) => {
drops.push(
this.teamPageService.getInformation('');
);
return drops;
})
)
}),
But still no luck.
Can some help me? how can I fix it?
Please take a look this image. Want to know how could I do this using cypress?
You can use something like this depending on what exactly are you looking for:
cy.get('table')
.find('tbody tr')
.then(trs => {
const content = [];
Cypress.$(trs).each((_, tr) => {
const row = [];
const tds = Cypress.$(tr).find('td');
Cypress.$(tds).each((_, td) => {
const text = Cypress.$(td)
.contents()
.last()
.text();
row.push(text);
});
content.push(row);
});
console.log(content);
});
I try to retrieve datas in a subcollection based on the key received on the first call.
Basically, I want a list of all my user with the total of one subcollection for each of them.
I'm able to retrieve the data from the first Payload, but not from pointRef below
What is the correct way to achieve that?
getCurrentLeaderboard() {
return this.afs.collection('users').snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data()
const id = a.payload.doc.id;
const pointRef: Observable<any> = this.afs.collection('users').doc(`${id}`).collection('game').valueChanges()
const points = pointRef.map(arr => {
const sumPoint = arr.map(v => v.value)
return sumPoint.length ? sumPoint.reduce((total, val) => total + val) : ''
})
return { id, first_name: data.first_name, point:points };
})
})
}
I tried to put my code in a comment, but I think it's better formated as a answer.
First you need subscribe your pointRef and you can change your code like this.
getCurrentLeaderboard() {
return this.afs.collection('users').snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data()
const id = a.payload.doc.id;
const pointRef: Observable<any> = this.afs.object(`users/${id}/game`).valueChanges() // <--- Here
const pointsObserver = pointRef.subscribe(points => { //<--- And Here
return { id, first_name: data.first_name, point:points };
})
})
}
....
//Usage:
getCurrentLeaderboard.subscribe(points => this.points = points);
And if you going to use this function alot, you should start to denormalize your data.
I have three subject. like this:
const s1$ = new Subject()
const s2$ = new Subject()
const s3$ = new Subject()
these three subjects call next() emit same value: const fruit = {id: 1, name: apple};
and, I have three methods to handle the logic one to one correspondence of the subjects call next(fruit) method.
method1() {
//called when s1$.next(fruit)
}
method2() {
//called when s2$.next(fruit)
}
method3() {
//called when s3$.next(fruit)
}
I want to implement this:
// here maybe not Observable.merge, it's just a thinking.
Observable.merge(
s1$,
s2$,
s3$
)
.doSomeOperator()
.subscribe(val => {
//val could be s1$ emit, s2$ emit or s3$ emit
//but the val is same, the fruit.
//do some map like s1->method1, s2->method2, s3->method3, so I can omit if...else statement.
const method = this.method1 | this.method2 | this.method3.
method();
})
How can I implement this, thanks.
Use map operator to add a distinguish sources.
export class AppComponent {
s1(val) {
console.log('s1', val);
}
s2(val) {
console.log('s2', val);
}
constructor() {
const s1= new Subject();
const s2= new Subject();
const m1= s1.map(val=> ({val, source:'s1'}));
const m2 = s2.map(val=> ({val, source:'s2'}));
Observable.merge(m1, m2)
.subscribe(({val, source}) => {
this[source](val);
});
s1.next('apple');
s2.next('apple');
}
}
If there are no priority order (given that no matter which Subject emits, you want to have the method called), I would suggest the following:
// here maybe not Observable.merge, it's just a thinking.
Observable.merge(
s1$.map((val) => ({fn: (arg) => this.method1(arg), val})),
s2$.map((val) => ({fn: (arg) => this.method2(arg), val})),
s3$.map((val) => ({fn: (arg) => this.method3(arg), val}))
)
.subscribe({fn, val}=> {
fn(arg)
});
You can also execute them at the map operator. But well, it depends what you are trying to achieve here