Cypress : How to use variables inside each function - cypress

I have a local variable that I am trying to access within the each function as below :
var i = 0
cy.get('body').then((body) => {
cy
.get('.classname')
.each(($element) => {
cy.log(i) //returns empty value
///ACCESS THE "i" variable here
}
}
How can the local variable be accessed inside the each function? Is there a restriction on the scope of the variables inside the each function

You can access the i variable inside the .each() but cy.log() will capture the initial value only.
console.log will show you the current value.
const texts = ['abc', '123']
cy.get('body').then((body) => {
cy.get('.classname')
.each(($element, i) => {
cy.wrap($element).should('contain.text', texts[i])
}
}

You cannot chain off each to cy. Cypress Docs
You can use it like this:
var i = 0
cy.get('selector').each(($ele) => {
cy.log(i) //prints 0
})

Related

Return text from function cypress

I need to return extracted text in function. I'm extracting text but cannot return it to use in functions
getAmounttxt() {
cy.get('#product_price_1_1_0 > .price').invoke('text').as('ame')
cy.get('#ame').then((ame) => {
cy.log(ame)
})
Cypress commands are asynchronous and thus do not compute a value directly, so you can't return a value from a function using a cypress command.
What you can do is return a Chainable and obtain a value within a then callback:
getAmounttxt() {
return cy.get('#product_price_1_1_0 > .price').invoke('text')
}
it('test', () => {
getAmounttxt().then(ame => {
cy.log(ame)
})
})
Also, you can use an alias to transfer you values. Please look into alias wrapper approach if you want to have an alias represented as a real variable which can be passed through all your code.
Just use your alias as the returned value, it is there to help with asynchronous commands.
getAmounttxt() {
cy.get('#product_price_1_1_0 > .price').invoke('text').as('ame')
}
// in test
getAmounttxt();
cy.get('#ame').then((ame) => {
cy.log(ame)
})

Retrieve value from json based on key provided using cypress

let expectedKey = 'Student';
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
if(expectedKey === 'Student'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.studentCode);
}
if(expectedDKey === 'Department'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.departmentCode);
}
if(expectedKey === 'Paper'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.paperCode);
}
if(expectedKey === 'Results'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.resultsCode);
}
}
I don't want to use these many if blocks as there will more keys in the future. Instead, I have to pick the required value for studentCode, departmentCode, paperCode, or resultsCode from JSON based on expectedKey. Any help please?
You can access object properties by dot notation (foo.bar) or bracket notation (foo['bar']). In your case, you'll have to ensure expectedKey matches a valid key in your object with assertion before the cy commands.
let expectedKey = 'studentCode';
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
expect(appDetails, 'valid key').to.have.property(expectedKey)
cy.get('app-screen').find('#code-details').should('have.text', appDetails[expectedKey]);
}
Assuming that you have the expectedKey inside the cy.readFile(), you can do like this:
Create a custom command at cypress/support/commands.js:
Cypress.Commands.add('codeDetailsText', (expectedKey, appDetails) => {
expectedKeyCode = expectedKey.toLowerCase() + 'Code'
cy.get('app-screen')
.find('#code-details')
.should('have.text', appDetails[expectedKeyCode])
})
In your test just write:
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
//Assuming expectedKey value is available here
cy.codeDetailsText(expectedKey, appDetails)
})

Mocha and Chai: JSON contains/includes certain text

Using Mocha and Chai, I am trying to check whether JSON array contains a specific text. I tried multiple things suggested on this site but none worked.
await validatePropertyIncludes(JSON.parse(response.body), 'scriptPrivacy');
async validatePropertyIncludes(item, propertyValue) {
expect(item).to.contain(propertyValue);
}
Error that I getting:
AssertionError: expected [ Array(9) ] to include 'scriptPrivacy'
My response from API:
[
{
"scriptPrivacy": {
"settings": "settings=\"foobar\";",
"id": "foobar-notice-script",
"src": "https://foobar.com/foobar-privacy-notice-scripts.js",
}
You can check if the field is undefined.
If field exists in the JSON object, then won't be undefined, otherwise yes.
Using filter() expresion you can get how many documents don't get undefined.
var filter = object.filter(item => item.scriptPrivacy != undefined).length
If attribute exists into JSON file, then, variable filter should be > 0.
var filter = object.filter(item => item.scriptPrivacy != undefined).length
//Comparsion you want: equal(1) , above(0) ...
expect(filter).to.equal(1)
Edit:
To use this method from a method where you pass attribute name by parameter you can use item[propertyName] because properties into objects in node can be accessed as an array.
So the code could be:
//Call function
validatePropertyIncludes(object, 'scriptPrivacy')
function validatePropertyIncludes(object, propertyValue){
var filter = object.filter(item => item[propertyValue] != undefined).length
//Comparsion you want: equal(1) , above(0) ...
expect(filter).to.equal(1)
}

Why does `react-hooks/exhaustive-deps` lint rule trigger on nested object properties?

I'm trying to use React hooks for memoizing a callback. This callback specifically uses a function that's defined on an object.
const setValue = useCallback((value) => {
field.setValue(key, value);
}, [field.setValue, key]);
This triggers Eslint rule react-hooks/exhaustive-deps.
It wants me to instead pass in [field, key].
If I then change the code to the following, I get no warning even though it seems equivalent:
const { setValue: setFieldValue } = field;
const setValue = useCallback((value) => {
setFieldValue(key, value);
}, [setFieldValue, key]);
Why is Eslint warn me in the first example?
Can I safely ignore it in such circumstances?
Try this.
const setValue = useCallback((value) => {
const set = field.setValue;
set(key, value);
}, [field.setValue, key]);
But it's not recommended.Take a look at this explanation. https://github.com/facebook/react/issues/15924#issuecomment-521253636

How can the evaluation of a ngrx-store selector be controlled?

I have a selector:
const mySelector = createSelector(
selectorA,
selectorB,
(a, b) => ({
field1: a.field1,
field2: b.field2
})
)
I know the selector is evaluated when any of its inputs change.
In my use case, I need to control "mySelector" by a third selector "controlSelector", in the way that:
if "controlSelector" is false, "mySelector" does not evaluate a new value even in the case "selectorA" and/or "selectorB" changes, and returns the memoized value
if "controlSelector" is true, "mySelector" behaves normally.
Any suggestions?
Selectors are pure functions..its will recalculate when the input arguments are changed.
For your case its better to have another state/object to store the previous iteration values.
You can pass that as selector and based on controlSelector value you can decide what you can return.
state : {
previousObj: {
...
}
}
const prevSelector = createSelector(
...,
(state) => state.previousObj
)
const controlSelector = createSelector(...);
const mySelector = createSelector(
controlSelector,
prevSelector,
selectorA,
selectorB,
(control, a, b) => {
if(control) {
return prevSelector.previousObj
} else {
return {
field1: a.field1,
field2: b.field2
};
}
}
)
Sorry for the delay...
I have finally solved the issue not using NGRX selectors to build up those "higher selectors" and creating a class with functions that use combineLatest, filter, map and starWith
getPendingTasks(): Observable<PendingTask[]> {
return combineLatest(
this.localStore$.select(fromUISelectors.getEnabled),
this.localStore$.select(fromUISelectors.getShowSchoolHeadMasterView),
this.memStore$.select(fromPendingTaskSelectors.getAll)).pipe(
filter(([enabled, shmView, tasks]) => enabled),
map(([enabled, shmView, tasks]) => {
console.log('getPendingTasks');
return tasks.filter(task => task.onlyForSchoolHeadMaster === shmView);
}),
startWith([])
);
}
Keeping the NGRX selectors simple and doing the heavy lifting (nothing of that in this example, though) in this kind of "selectors":
- will generate an initial default value (startWith)
- will not generate new value while filter condition fails (that is, when not enabled, any changes in the other observables do not fire a new value of this observable)

Resources