How to take value from the element in the Cypress tool? - javascript

I have a problem with taking value of the element like:
<div class="Test">Number 10</div>
Let say, that I have 10-20 classes with values like here, then I can use:
cy.get('.Test').invoke('text').as.('Field1')
to get proper value, but it is only possible in different test by using this.Field1.
How to get value in the same test without using:
then and .text()?
Is it possible? I have many fields and I would like do it in one single test to check proper values in the next view.
Did anyone has similar problem? Thanks

It sounds as though you may want to use .its
cy.get('selector').its(propertyName).should('contain', 'string')
its needs to be chained off a previous command like get. You can also use this in a function as per Cypress's example
Cypress shows an example for DOM elements
Get the length property of a DOM element
cy
.get('ul li') // this yields us a jquery object
.its('length') // calls 'length' property returning that value
.should('be.gt', 2) // ensure the length is greater than 2
})
You can access functions to then drill into their own properties
instead of invoking them.
// Your app code
// a basic Factory constructor
const Factory = (arg) => {
// ...
}
Factory.create = (arg) => {
return new Factory(arg)
}
// assign it to the window
window.Factory = Factory
cy
.window() // yields window object
.its('Factory') // yields Factory function
.invoke('create', 'arg') // now invoke properties on itv
You can drill into nested properties by using dot notation.
const user = {
contacts: {
work: {
name: 'Kamil'
}
}
}
cy.wrap(user).its('contacts.work.name').should('eq', 'Kamil') // true
For the full list of examples and rules check out Cypress.io documents https://docs.cypress.io/api/commands/its.html#Syntax

Related

How to render multiple HTML parts with a plain Javascript function

This is a static website with hundreds of pages. I need to render elements like a topnav or a newsletter or a strap of content and changing those contents periodically, from JS.
This is what I tried:
const components = {
compartirEnFlex: `<h4>Newsletter</h4>`,
newsletterEs: `<h4>Compartir</h4>`,
}
const ids = ['newsletterEs', 'compartirEnFlex', 'infoArticulo', 'infoDeLaWebEnFlexIzq']
function renderComponents(objWithComp, idsArr){
return idsArr.map(function(id){
for(let component in objWithComp){
let arrOfIds = Object.keys(objWithComp);
arrOfIds.map(key => key)
if(id === key){
document.getElementById(id).insertAdjacentHTML('afterbegin', objWithComp[id])
}
}
})
}
renderComponents(components, ids);
Each id has its counterpart in the HTML structure. When I do this individually it works. However, I have to handle this in an elegant way (and there is no possibility for a JS framework like React in this project).
Thanks for the help!
When you run your code, you'll see the error Uncaught ReferenceError: key is not defined in the console.
That's because key in if(id === key) is not defined. The line arrOfIds.map(key => key) returns the same exact array as arrOfIds because Array.prototype.map "returns a new array populated with the results of calling a provided function on every element in the calling array."
Here, you don't assign that new array to a variable, so nothing happens. Even if it was, that new array would be a copy of arrOfIds because your mapping function (key) => key returns key for every key -- meaning that the output is the same as the input.
However, that's not an issue here. If I understand your question correctly, then this demo should show an example of what you're trying to accomplish. If that's what you want to achieve, then here's a solution:
First, you don't need to iterate for component in objWithComponent inside idArr -- you're already doing that in the idArr. You don't need the ids array either, because you can get the keys of the components from the components object using Object.keys().
Let's say your HTML looks something like this:
<div>
<div id="newsletterEs"></div>
<div id="compartirEnFlex"></div>
<div id="infoArticulo"></div>
<div id="infoDeLaWebEnFlexIzq"></div>
</div>
Then, using Object.keys(components) to get an array of the ids of the components that you have, you can map those to HTML tags. In fact, map is not necessary here because map returns a new array, and unless you need that array later, there's no reason to use map. Instead, you can use Object.prototype.forEach.
Here's what that would look like:
const components = {
compartirEnFlex: `<h4>Newsletter</h4>`,
newsletterEs: `<h4>Compartir</h4>`,
}
function renderComponents(objWithComp) {
Object
.keys(components)
.forEach((id) => {
const element = document.getElementById(id)
const component = objWithComp[id]
if (component && element) {
element.insertAdjacentHTML('afterbegin', component)
}
})
}
renderComponents(components)
Then, when you call renderComponents, you can pass just the components argument, and only render the components for which divs with corresponding ids exist with an if statement.

iteration of a JSON containg objects

I am trying to write a function that takes pokemon’s name as an argument and find out which all pokemon have that name in their “next_evolution” field
Consider the following JSON dataset -
visit https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json
now for that, I've written the following function:
var infoOfPokemon = function(nameOfPokemon,allPokemon){
for(x in allPokemon){
if(allPokemon[x].next_evolution.includes(nameOfPokemon)){
console.log('pokemons found: '+allPokemon[x].name)
} else{
null
}
}
}
var nameOfPokemon =prompt('enter the name of Pokemon')
infoOfPokemon(nameOfPokemon,pokemonData.pokemon)
but it is returning an error which says
Uncaught TypeError: Cannot read property 'includes' nextEvolution.js:4090 of undefined
One or more of your pokemon does not have a 'next_evolution' field set (for example, the one with id 3 in your file). Hence allPokemon[x].next_evolution evaluates to undefined and you cannot read 'includes' on that.
Check for the existence of next_evolution, first.
if (allPokemon[x].next_evolution &&
allPokemon[x].next_evolution.includes(nameOfPokemon)) { ...
and find out which all pokemon have that name in their “next_evolution” field
That should make you think of filter. You want to filter the list of pokemon to find the matching ones.
But, as already pointed out, you need to be able to return false in your filter predicate if the pokemon you're testing does not have a next_evolution field. You can do that via testing, as suggested in another answer: pokemon.next_evolution && pokemon.next_evolution.includes..., or you can default it, as below: (pokemon.next_evolution || []).includes...
(Note though that both you original and that answer miss a step, which is to then collect that name properties from those next_evolution values.
So here is a simple function which should filter the list to find the correct items. To use it with a call to your API, I wrap it in a fetch call, and simply console.log the results. Obviously, you might well have other ways to call it.
const infoOfPokemon = (name, all) => all.filter(pokemon =>
(pokemon.next_evolution || []).map(p => p.name).includes(name)
)
fetch('https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json')
.then(resp => resp.json())
.then(allPokemon => console.log(
infoOfPokemon('Butterfree', allPokemon.pokemon) //~> [{Caterpie}, {Metpod}]
))
You cannot use includes function directly on array having objects instead of single object. It will give false as output.
For e.g:
var pets = [{'name':'cat'},{'name':'dog'},{'name':'bat'}];
console.log(pets.includes("cat")) //false
This results in false always.
Proper way is to use a map to get a particular object field on which you want to use includes function.
e.g:
var pets = [{'name':'cat'},{'name':'dog'},{'name':'bat'}];
console.log(pets.map((obj)=> obj.name).includes("cat")); //true
In Your case first see if allPokemon[x].next_evolution is undefined or not (i.e next_evolution key is present or not)
code becomes:
if (allPokemon[x].next_evolution &&
allPokemon[x].next_evolution.(obj)=> obj.name).includes(nameOfPokemon)) { ...

"Sync" two JS objects in Vue.js

I have 2 JS objects, each is rendered as Tree in a webpage.
My issue is how to force a change on one of them while user applies a change to others.
My basic idea is to "bind onChange" on each objects obviously paying attention to not generate infinite loops.
In jQuery it seems almost difficult, I read something about "proxy" but I don't understand if it could help me on this topic.
I lastly thought to vue.js. I read that vue.js is very efficient syncing js and dom objects so a change between them is almost easy, maybe is possible to sync two js objects?
To be clearer, here more details:
I have something like this:
let obj1={key1:1, key2:[1,2,3]}; // defines arbitrary data obj
let obj2={};
$.extend(obj2,obj1); // defines obj2 as clone of obj1
// do "something magic" here
I would like to get the following:
obj1.key1=2; // => should automatically set obj2.key1=2; under the hood
obj2.key2.push(4); // => should automatically set obj1.key2=[1,2,3,4] under the hood
Is there any trick to bind two (identical, cloned) data objects so that any change made on one of them is reflected to the other one, as if the involved object keys "pointed" to the same data? Since objects are assigned "by reference" in javascript, this is doable if we define a third object "obj_value" and we assign it as value to the above objects as follows:
obj1.key=obj_value; // both obj1.key and obj2.key point to the same object
obj2.key=obj_value;
But I'd like something more general, directly binding one obj key to the other, in pseudo-code:
obj1.on('change',function(key,value)
{
obj2.key=value;
})
watch: {
objChangedByUser: function (value) {
this.cloneOfobjChangedByUser = Object.assign({}, value);
}
}
Or:
computed: {
cloneOfObjChangedByUser: function () {
return Object.assign({}, this.objChangedByUser);
}
}

When does the javascript console evaluate the contents of a printed object?

I have found that when using console.log on a nested object the result seems to print after subsequent operations have performed, but only when logging an object with a depth of at least two.
Consider the following example:
function addNumbers(data) {
data.container.firstNumber++
console.log(data); // 3
data.container.firstNumber++
}
var numberData = {
container : {
"firstNumber": 1
}
};
console.log(numberData); // 3
addNumbers(numberData);
I assumed that with this example I would see numberData printed with the values 1 and then 2. In actuality, the value is 3, 3. My first thought was perhaps there is some race condition here where the values changes before the log operation can complete, but if you add more specific references to the object, you will get the value that matches the console.log's position in the code like this:
function addNumbers(data) {
data.container.firstNumber++
console.log(data.container); // 2
data.container.firstNumber++
}
var numberData = {
container : {
"firstNumber": 1
}
};
console.log(numberData.container); // 1
addNumbers(numberData);
Why doesn't the variable evaluate to its value relative to the position of the console.log in the first example, but does in the second example?
relevant fiddle
Most browsers only fetch the properties of an object logged to the console when you expand the object -- and often note this in a tooltip next to the object reference.
They also tend to only expand the object the first time and cache from then on, so the console is not a reliable way to inspect objects.
To get around this, browsers offer a watch panel that will update more frequently and can be used with breakpoints or stepping to get the current state of an object repeatedly.

Javascript/ Jquery Select object based on nested attribute

I'm trying to retrieve a nested object based on a nested nested id.
So my object is as follows
object = {
1: {
feature: {id:"1012"},
}
2: {
feature: {id:"3032"}
}
}
I have an id and I need to retrieve the corresponding object or to be more specific the object id. The object is a lot more complex but above shows the values that I need to retrieve.
I don't have much experience in JavaScript so am unsure how to achieve this. I've tried using Jquery's attribute selectors but have not been successful.
Any help would be appreciated.
Thanks.
if your "id" is mean like 1 or 2
do it like this:view it in JSFiddle
var obj = {
1: {
feature: {id:"1012"}
},
2: {
feature: {id:"3032"}
}
}
var getById = function(id){
return obj[id];
}
alert(getById(1).feature.id);
another way,if your id means like '1012','3032'
do it like this:view it in JSFiddle
my post about the Map in js
If I'm understanding the question correctly you are trying to use the id property of the object in each feature property to get the key (1, 2, etc) from object? So if you entered "1012" you would get back 1, if you entered "3032" you would get 2, etc?
If so this would do it:
var object = {
1: {
feature: {id:"1012"}
},
2: {
feature: {id:"3032"}
}
},
getIdByFeatureId = function (featureId) {
var id,
subObject;
// loop through each property of the object
for (id in object) {
// protect ourselves in case someone has tampered with Object.prototype
if (object.hasOwnProperty(id)) {
subObject = object[id];
if (subObject.feature.id === featureId) {
return id;
}
}
}
// none found? return null.
return null;
};
getIdByFeatureId("3032"); // returns 2
getIdByFeatureId("1012"); // returns 1
getIdByFeatureId("90210"); // returns null
You can play with the code in this fiddle.
Numbers stored as strings can be a pain, and often lead to confusion in how one need to call a function like this. One thing you might notice is I used the === strict equal operator. This only returns true if both values are exactly the same, including their type. It's good practice to use strict comparison operators unless you absolutely can't. It is also slightly faster since it doesn't have to coerce the values into a like type. But that means that you must pass a string into the function in order for it to match. You could use the non-strict equals == if you need it to be more flexible. If all of the feature ids are numeric (and none of them have leading zeros) and you have the ability to, I would change the feature ids to be actual numbers so you can keep it more intuitive by just passing in a number instead of a string representation of a number: getIdByFeatureId(3032); while keeping the strict comparison.

Categories

Resources