Cypress - get value that is inside a component (into a variable) - javascript

I'm a new developer and I am trying to build a certain test using cypress. the client has a dynamic grid, and I'd like to see which row in the grid contains the data I want. I cannot change the HTML file.
At the moment I am trying to iterate over all rows using each() cypress command. So I have two questions:
is this a correct approach?
assuming it is correct, how do I get the value out of the component? for example, how do I get the value of "aaaaaaaa" out of here (and into a variable):
html structure
thanks!

Using each is a pretty good approach, select the rows and make use of the index parameter to find which one.
cy.get('[role="row"]').each(($row, index) => {
if ($row.find('span').text() === 'aaaa') {
cy.wrap(index).as('rowIndex')
})
cy.get('#rowIndex')
.then(rowIndex => {
// use the row index here
})
There's also this method Cypress get index of th element to use it later.
If it works (the jQuery docs don't show this usage) the code for your HTML would be
cy.contains('[role="row"]', 'aaaa').invoke('index').as('rowIndex')
cy.get('#rowIndex')
.then(rowIndex => {
// use the row index here
})

There are a number of approaches you can take, but I think my preferred one is to store the variable in Cypress environment variable. In the following, I get the element, then use a .then() statement to get the text of the element (with the JQuery function, .text()), and store it in a Cypress environment variable (via Cypress.env()).
describe('tests', () => {
it('test', () => {
cy.get('some-element').then(($el) => {
Cypress.env('myVar', $el.text());
})
... // some code
// When needing the variable, reference it by Cypress.env('myVar');
});
afterEach(() => {
// after each test, clear the variable
Cypress.env('myVar', null)
});
});

I have a test doing the same thing and the way I've approached it was:
cy.contains('aaaa').invoke("text").as('variableName')
This can now be used like this:
cy.get(this.variableName).should('be.visible')
cy.contains('aaaa').parent().next().children() //Used to iterate through rows.

Related

Protractor test: Is it able to block/freeze the DOM during test execution?

I'm executing the Protractor test in angular web application.
Test Case:
Find elements in the list.
Loop through every element in the list.
If an element contains the required name.
Click at the element.
Code:
let projectsList = await element.all(by.css(Selectors.projectList));
for (item of projectsList) {
item.getText().then((text) => {
if (text.includes("50_projects_to_tests")) {
console.log(text)
item.clik()
}
}, (err) => console.log(err));
}
Problem:
The test case is straightforward to execute except one thing.
Request about updating information in the project is sending every few second.
When the response back from the server I'm loosing the selected list of projects before.
It means that I'm not able to click at the element which I found because the element no longer exists.
I receiving:
StaleElementReferenceError: stale element reference: element is not attached to the page document
Question:
Is it possible to block/freeze the DOM while the test is executing?
Any ideas would be appreciable to handle the issue.
Getting stale element references while looping is a common problem
First note is that you should try avoid using .then() for managing promises if you already are using async/await, it just makes things more difficult to read.
Secondly I would caution against disabling the refresh if that's not how the applicaiton works when a enduser will be interacting with it.
The following answer is based on the assumption that after the page refreshes the same elements will be found by element.all(by.css(Selectors.projectList));. In this answer the whole element array is recaptured during each loop but it stores the index value of the element it needs so the loop proceeds
let projectsList = await element.all(by.css(Selectors.projectList));
for(let loopCount = 0; loopCount < projectsList.length; loopCount++){
projectsList = await element.all(by.css(Selectors.projectList));
const item = projectsList[loopCount];
const itemText = await item.getText();
if (itemText.includes("50_projects_to_tests")) {
console.log(itemText )
item.clik()
}
}

Protractor - getting computed style from web element using browser.executeScript works with string but fails with function

I'm trying to verify visibility for a tooltip popup by calling window.getComputedStyle().visibility property using protractor framework.
When I pass a string to executeScript it's working fine. It's returning visible:
// elementToCheck is an ElementFinder
async getComputedStyleVisibility(elementToCheck) {
return await browser
.executeScript(`return window.getComputedStyle(document.querySelector('${elementToCheck.locator().value}')).visibility`);
}
However, this is failing when I replace the string within executeScript by a function. It's returning hidden and it looks like execution gets stuck until tooltip popup disappears.
So I guess there's some synchronisation issue, but I cannot figure out what's happening:
// elementToCheck is an ElementFinder
async getComputedStyleVisibility(elementToCheck) {
return await browser.executeScript(
webElem => (window.getComputedStyle(webElem).visibility),
await elementToCheck.getWebElement()
);
}
For making your script work as you wanted, you need to correctly access your webElement.
The docs say,
Any arguments provided in addition to the script will be included as script arguments and may be referenced using the arguments object
So you need to use arguments object in your script. Like so:
async getComputedStyleVisibility(elementToCheck) {
return await browser.executeScript(
() => (window.getComputedStyle(arguments[0]).visibility),
await elementToCheck.getWebElement()
);
}
BUT
If you aren't restricted in some way to only use browser.executeScript() then you should overthink your approach.
protractor provides an API for checking if a certain element is present or similar.
Check an element to be present and visible for user:
element(by.css("#a")).isDisplayed()
.then(isDisplayed => console.log("element displayed?", isDisplayed))
.catch(err => console.error("Some error happedn. Element not present..", err))
You should use browser.executeScript() only as last resort in my opinion. Most of the general stuff like clicking, checking if present, etc. is already there, in a handy way, provided by protractor.

When passing a collection, only the first element seems to be passed

I am using my code to get data from JIRA. JIRA server returns a set of elements (called issues), and this works perfectly fine:
for (let issue of JiraIssues) {..processing code } //this works well, I get all the issues
However, when I wanted to separate the code getting the data from the code processing it, I cannot figoure out how to pass the JiraIssues correctly:
Processing(JiraIssues) //calling the function
function Processing(jiraIssues) //fucntion signature
The thing is, what is passed into the function seems to be just the first element.
function processOneIssue(issue) {
console.log(issue); // logs one issue
}
function processAllIssues(issueList) {
console.log(issueList); // logs the list with all the issues
}
JiraIssues.forEach((issue) => {
this.processOneIssue(issue);
});
this.processAllIssues(JiraIssues);
I am not sure if I understand your problem. But this two little function should do what you need. Make sure you are calling them in the right place. It is possible that you are overwriting the original JiraIssues inside the loop with one of the element. Try using different less familiar variable names for testing purposes.
UPDATE based on the comment:
//// file1
export function processOneIssue(issue) {
console.log(issue); // logs one issue
}
export function processAllIssues(issueList) {
console.log(issueList); // logs the list with all the issues
}
////// file 2
import * as file1 from 'path1';
JiraIssues.forEach((issue) => {
file1.processOneIssue(issue);
});
file1.processAllIssues(JiraIssues);
There should be no diffrence just because the import.

Custom and first options in ember-power-select

I'm using Ember-power-select-with-create to make custom selections possible in my dropdowns.
This is the power-select part in the .hbs file.
{{#power-select-multiple-with-create
options=options //array for prefill values
selected=offer.offer //is where we save it to (offer is the model and has an array named offer)
onchange=(action (mut offer.offer)) //default onchange for prefill options
onfocus=(action "handleFocus")
onblur=(action "handleBlur")
oncreate=(action "createOffer" offer.offer) //This calls "createOffer"
buildSuggestion=suggestion
as |offer|}}
{{offer}}
{{/power-select-multiple-with-create}}
The "createOffer" function in my .js file looks like this:
It gets two values passed: selected which is offer.offer and searchText which is the input that the user is trying to add.
createOffer(selected, searchText)
{
if (selected == null)
{
selected = []; //This is what I'm currently trying: if its null initialize it
}
if (selected.includes(searchText)) { //Don't add if it's already added
this.get('notify').alert("Already added!", { closeAfter: 4000 });
}
else { // if it's not yet added push it to the array
selected.pushObject(searchText);
}
}
This code works perfectly fine for adding those that we predefined as well for custom options, however it does not work if it is the first and a custom offer that we try to add to a new group of offers.
I'm assuming it has something to do with the fact that the array is not yet initialized. A pointer to the fact that it is due to the array not being initialized is that I get an error message along the lines of: Can't call pushObject on undefined. And I'm assuming the reason that it works with predetermined values is that ember-power-select probably initializes it somewhere but I couldn't find any documentation about it.
Thanks for your answers!
If anyone ever stumbles upon this, this was the solution
Calling that when we create the offer and then initializing the offer array like that:
offer.set('offer', []);

Create basic library firebase error

I have developed some applications using Firebase. Over time, I realized that I am repeating a lot of unnecessary code and decided to create a small library to help me increase productivity. Right at the beginning, I tried to create this object in Javascript:
read.childRoot = function(att) {
var acessChildRoot = firebase.database().ref("root/");
acessChildRoot.once('value').then(function(snapshot) {
alert(snapshot.child("nome").val());
});
}
And I tried to access through this line of code:
alert(read.childRoot("nome"));
So I was able to read the reference I wanted, but the first return was the undefined value. How can I filter this value and just display the value I really want to see?
It seems that you want to wait for the first value to be set on a node.
In that case, I recommend using this snippet (from my gist):
var listener = ref.on('value', function(snapshot) {
if (snapshot.exists()) {
console.log('Value is now '+snapshot.val());
ref.off('value', listener);
}
});

Categories

Resources