I am new to Cypress and trying to loop the swiper slides excluding cloned duplicates. I am using the index of .each() in cypress but this is not working. Below is my code
if (index != 0 && index >= 22) {
//do something
} else {
//do something
}
Below is the example snapshot of my html code:
Can anyone please suggest the logic to loop only into the original slides?
You can use the :not() pseudo-selector
cy.get('div.swiper-slide:not(.swiper-slide-duplicate)')
.should('have.length', 23) // to show loop is filtered, remove once confirmed
.each($swiperSlide => {
...
or if you prefer to check inside the loop use .not() method
cy.get('div.swiper-slide')
.each($swiperSlide => {
if ($swiperSlide.not(".swiper-slide-duplicate").length) {
} else {
}
})
You can use the jquery hasClass() method with negation to check that the class .swiper-slide-duplicate is not present.
cy.get('div.swiper-slide').each(($ele) => {
if(!$ele.hasClass(".swiper-slide-duplicate")){
//Do something when you don't have the duplicate slide
}
else {
//Do something when you have the duplicate slide
}
})
You can also reverse the check as well, first, you check the presence of the class .swiper-slide-duplicate and then in the else condition you can mention the things when you don't have the duplicate slide.
cy.get('div.swiper-slide').each(($ele) => {
if($ele.hasClass(".swiper-slide-duplicate")){
//Do Something when you have the duplicate slide
}
else {
//Do Something when you don't have the duplicate slide
}
})
You can use the jQuery .attr() method to check if the class is just swiper-slide or has both swiper-slide and swiper-slide-duplicate.
cy.get('div.swiper-slide')
.each($el => {
if($el.attr("class") === "swiper-slide") {
}
else {
}
})
Same approach for data-swiper-slide-index
cy.get('div.swiper-slide')
.each($el => {
if($el.attr("class") === "swiper-slide" && $el.attr("data-swiper-slide-index") !== "0") {
}
else {
}
})
You can use a jQuery .filter() to process each type
cy.get('div.swiper-slide')
.then($els => {
// Process non-duplicates
$els.filter('[class=["swiper-slide"]') // exact match to a single class
.each($el => {
})
// Process duplicates
$els.filter('[class*=["swiper-slide-duplicate"]') // *= means "contains"
.each($el => {
})
})
or in Cypress commands (without jQuery)
// Process non-duplicates
cy.get('div.swiper-slide')
.filter(':not(".swiper-slide-duplicate")')
.each($el => {
...
})
// Process duplicates
cy.get('div.swiper-slide')
.filter('.swiper-slide-duplicate')
.each($el => {
...
})
To loop only into the original slides, use the Cypress .not() command to exclude duplicates.
cy.get('div.swiper-slide')
.not('.swiper-slide-duplicate')
.each($el => {
...
})
Related
I have been trying to create my own custom chai assertion (based on the Cypress recipe template: https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/extending-cypress__chai-assertions/cypress/support/index.js).
What I have found with the code below is that when it is run I end up with a constant loop of WRAP, if I swap this.obj with element it then results in a constant stream of GET. I do not seem to ever progress further than getRect(first).then((actual)
If anyone could help me out I'd be very grateful.
cypress/integration/test.js
describe('testing custom chai', () => {
it('uses a custom chai helper', () => {
cy.visit('https://www.bbc.co.uk/news');
cy.get('#orb-modules > header').should('be.leftAligned', '#orb-header');
});
});
cypress/support/index.js
function getRect(selector) {
if (selector === '&document') {
return cy.document().then(doc => doc.documentElement.getBoundingClientRect());
} if (typeof selector === 'string') {
return cy.get(selector).then($elem => $elem[0].getBoundingClientRect());
}
return cy.wrap(selector).then(elem => Cypress.$(elem)[0].getBoundingClientRect());
}
function getRects(first, second) {
return getRect(first).then((actual) => {
getRect(second).then(expected => [actual, expected]);
});
}
const aligned = (_chai, utils) => {
function leftAligned(element) {
getRects(element,this.obj).then((rects) => {
this.assert(
rects[0].left === rects[1].left,
'expected #{this} to be equal',
'expected #{this} to not be equal',
this._obj,
);
});
}
_chai.Assertion.addMethod('leftAligned', leftAligned);
};
chai.use(aligned);
The basic problem is that the async commands cy.get(), cy.wrap(), cy.document() can't be used in the custom assertion. My best guess is that the auto-retry mechanism is going bananas and giving you the constant loop.
Instead, you can use Cypress.$() which is the synchronous version (essentially jquery exposed on the Cypress object).
The following seems to work ok. (I renamed getRects() param to subject, as sometimes it's a selector and sometimes it's the object passed in to .should()).
Note also this._obj instead of this.obj.
function getRect(subject) {
if (subject === '&document') {
return Cypress.$(document).context.documentElement.getBoundingClientRect();
}
if (typeof subject === 'string') { // the selector passed in to assertion
return Cypress.$(subject)[0].getBoundingClientRect();
}
if (typeof subject === 'object') { // the element from cy.get() i.e this._obj
return subject[0].getBoundingClientRect();
}
return null; // something unkown
}
function getRects(first, second) {
const actual = getRect(first)
const expected = getRect(second)
return [actual, expected];
}
const aligned = (_chai, utils) => {
function leftAligned(element) {
const rects = getRects(element, this._obj)
this.assert(
rects[0].left === rects[1].left,
'expected #{this} to be equal',
'expected #{this} to not be equal',
this._obj,
);
}
_chai.Assertion.addMethod('leftAligned', leftAligned);
};
chai.use(aligned);
I was unable to test your BBC page directly, as there's a cross-origin problem occurring
Refused to display 'https://www.bbc.com/news' in a frame because it set 'X-Frame-Options' to 'sameorigin'
but it does work with a mockup page
cypress/app/bbc-sim.html
<div id="orb-modules">
<header>
<h1>Brexit: Boris Johnson's second attempt to trigger election fails</h1>
</header>
</div>
and testing like so
it('uses a custom chai helper', () => {
cy.visit('app/bbc-sim.html')
cy.get('#orb-modules > header').should('be.leftAligned', '#orb-modules');
});
how to get variable from outer layer method
trying to use a variable in outer layer in my React-Native App
updateCheckBox() {
Constants.TABS.map((item) => {//Constants.TABS is an array
AsyncStorage.getItem(item)//using item as key to fetch from AsyncStorage
.then((res) => {
if(res == 1) {
//debugged here, item was undeined. but i need setState here with item as key. How should i get item here.
this.setState({item: true}) // I need to get the item here, but it show undefined
} else {
this.setState({item:false}) // I need to get the item here, but it show undefined
}
})
})
}
// I need to get the item here, but it show undefined
You need to wrap the item in [] to use it as a key for a property. Like this:
updateCheckBox() {
Constants.TABS.map(item => {
AsyncStorage.getItem(key) //
.then((res) => {
//item is accessible here, to use item as the key to a property wrap it in []
if(res == 1) {
this.setState({[item]: true});
} else {
this.setState({[item]: false});
}
})
})
}
finally, I found there is no issue in this code, the thing is
updateCheckBox() {
Constants.TABS.map((item) => {
let key = item
AsyncStorage.getItem(key)
.then((res) => {
console.log(item, "item is here", res); //item is visible here
console.log(key) //key is all always undefined
if(res == 1) {
this.setState({item: true})
} else {
this.setState({item:false})
}
})
})
}
key is not visible in method then, which I can not explain, but all in all, my code works.
I am using fetch to get some data from an API, I convert this to JSON and want to sort it into different categories. For example tickets (which is what I'm retrieving) with the status active should be in a different array than the ones with status waiting on customer. I want to use a for loop to sort through the results. Should I do this in the same function they're fetched in?
Did a bit of googling but couldn't find a post on this.
methods: {
fetchTickets() {
fetch('/api')
.then(res => res.json())
.then(resJson => {
arrayLength = resJson.length
for(var i = 0; i < arrayLength; i++) {
if(resJson[i]['status'] === 'active') {
//do something
}
else if(resJson[i]['status'] === 'waiting on customer') {
// do something else
}
else {
// do a dance
}
}
});
},
}
So, is it okay to do the above or is it very sensitive to errors/is there a more convenient alternative?
There is a more convient alternative.
You should create two API calls.
1.) /api/activeUsers
2.) /api/waitingCustomers
Then for each API call, you can use the .filter API and return the appropiate array
fetchActiveTickets() {
fetch('/api')
.then(res => res.json())
.then(resJson => {
return resJson.filter(item => {
return item.status ==='active'
})
//do the same for waiting... i.e. resJson(item => {
//return item.status ==='waiting'
//})
}
});
},
I would recommend using .filter() rather than looping over the array to split the source into the pieces you want.
data: {
activeTickets: [],
waitingTickets: []
}
methods: {
fetchTickets() {
fetch('/api')
.then(res => res.json())
.then(resJson => {
this.activeTickets = resJson.filter(function(ticket) { return ticket.status === 'active' });
this.waitingTickets= resJson.filter(function(ticket) { return ticket.status === 'waiting on customer' });
// do things with your filters arrays...
});
},
}
Try
methods: {
async fetchTickets() {
let res = await (await fetch('/api')).json();
let active = res.filter(x=> x['status']=='active');
let waiting = res.filter(x=> x['status']=='waiting on customer');
// ... do something
},
}
I have a div with a conditional class that works well when the app is loaded, but it's not updated when the store data change.
The code in my vue component looks like this
<span class="week-day"
v-bind:class="{ complete: isDayComplete(day) }"
v-for="day in daysInWeek"
v-bind:data-day="day"
> </span>
And I have ...mapGetters(['isDayComplete']) in my computed object.
The getter looks like this
isDayComplete(state) {
return (day) => {
console.log(`called isDayComplete(${day})`)
const formattedDay = moment(day, 'DD/MM/YYYY').format('YYYY-MM-DD');
if (state.daysData[formattedDay]) {
if (state.daysData[formattedDay].meals.length > 0) {
console.log(`day ${day} is complete`);
return true;
} else {
console.log(`day ${day} is NOT complete`);
return false;
}
} else {
console.log(`no data for day ${day}`);
return false;
}
}
},
I update my meals data in a mutation
updateMeals(state, meals) {
_.forEach(meals, (meal) => {
state.daysData[meal.day].meals.push(meal);
});
}
And I have an action that commits that mutation
loadMeals({ state, commit }) {
return new Promise((resolve, reject) => {
get.meals.from.api()
.then((response) => {
commit('initDaysData');
commit('updateMeals', response.data.data);
return resolve();
})
.catch(reject);
});
}
So whenever I call loadMeals the class is not updated if one day changes its status (complete/not-complete). If I reload the page, the class is set correctly.
What am I doing wrong? Thanks!
It's a common reactivity problem. You can make deep copy (use JSON.parse(JSON.stringify())) to make data reactive:
updateMeals(state, meals) {
_.forEach(meals, (meal) => {
state.daysData[meal.day].meals.push(meal);
});
state.daysData = JSON.parse(JSON.stringify(state.daysData))
}
#ittus answer was correct. I found another way to achieve this that maybe could
help someone else.
add another mutation on the store
updateCompletedDays(state) {
const newState = [];
_.forEach(state.daysData, (currentDayData, currentDay) => {
if (currentDayData.meals.length > 0) {
newState.push(currentDay);
}
});
state.completedDays = newState;
},
commit this mutation after meals are updated
change isDayComplete getter to
isDayComplete(state) {
const formattedDay = moment(day, 'DD/MM/YYYY').format('YYYY-MM-DD');
return state.completedDays.indexOf(formattedDay) !== -1;
}
Basically when using reactivity going deep into arrays/object will not work, better have arrays of aggregated data (check also Vue.set api)
I am trying to find simple way to update the form fields with Validators. For now I do the below:
ngOnInit() {
this.form.get('licenseType').valueChanges.subscribe(value => {
this.licenseChange(value);
})
}
licenseChange(licenseValue: any) {
if (licenseValue === 2) {
this.form.get('price').setValidators([Validators.required]);
this.form.get('price').updateValueAndValidity();
this.form.get('noOfLicenses').setValidators([Validators.required]);
this.form.get('noOfLicenses').updateValueAndValidity();
this.form.get('licenseKey').setValidators([Validators.required]);
this.form.get('licenseKey').updateValueAndValidity();
this.form.get('supportNo').setValidators([Validators.required]);
this.form.get('supportNo').updateValueAndValidity();
this.form.get('purchasedFrom').setValidators([Validators.required]);
this.form.get('purchasedFrom').updateValueAndValidity();
//......others follows here
}
else {
this.form.get('price').clearValidators(); this.form.get('price').updateValueAndValidity();
this.form.get('noOfLicenses').clearValidators(); this.form.get('noOfLicenses').updateValueAndValidity();
this.form.get('licenseKey').clearValidators(); this.form.get('licenseKey').updateValueAndValidity();
this.form.get('supportNo').clearValidators(); this.form.get('supportNo').updateValueAndValidity();
this.form.get('purchasedFrom').clearValidators(); this.form.get('purchasedFrom').updateValueAndValidity();
//......others follows here
}
}
Is this the only way to add and update validators or is there any other way to achieve this. For now I am calling the updateValueAndValidity() after setting/clearing each field.
Update
Something like
licenseChange(licenseValue: any) {
if (licenseValue === 2) {
this.form.get('price').setValidators([Validators.required]);
//......others follows here
}
else{
//......
}
}
this.form.updateValueAndValidity();///only one line at the bottom setting the entire fields.
I done something similar like this
licenseChange(licenseValue: any) {
if (licenseValue === 2) {
this.updateValidation(true,this.form.get('price'));
//......others follows here
}
else {
this.updateValidation(false,this.form.get('price'));
//......others follows here
}
}
//TODO:To update formgroup validation
updateValidation(value, control: AbstractControl) {
if (value) {
control.setValidators([Validators.required]);
}else{
control.clearValidators();
}
control.updateValueAndValidity();
}
If you want to do this for all the controlls inside your form
licenseChange(licenseValue: any) {
for (const field in this.form.controls) { // 'field' is a string
const control = this.form.get(field); // 'control' is a FormControl
(licenseValue === 2) ? this.updateValidation(true,
control):this.updateValidation(fasle, control);
}
}
I did it like below:
this.form.get('licenseType').valueChanges.subscribe(value => {
this.licenseChange(value, this.form.get('price'));
//....Others
}
licenseChange(licenseValue: any, control: AbstractControl) {
licenseValue === 2 ? control.setValidators([Validators.required]) : control.clearValidators();
control.updateValueAndValidity();
}