Hi folks I am new into cypress
I have a dropdown checkbox button from where I have to select multiple values at once
For this I have created a local function in type script as below
function calling
selectItems('Item 1','Item 4')
function definition
selectItems(value1: any, value2: any){
cy.get('dropdownlocator').click();
cy.get('dropdownlocatorCheckboxItems').contains(value1).click();
cy.get('dropdownlocatorCheckboxItems').contains(value2).click()
}
This is working fine but what I wanted is instead of doing the hard coding for each value I should make it so generic that if I pass single value in param it will work or if I pass more than 2 values it should also work
There are two things that you can take a look at:
The arguments object
This is a hidden object of every function call, and can be used to figure out what arguments are passed in without defining what is passed in
selectItems() {
cy.wrap(arguments).each(value => {
cy.contains('dropdownlocatorCheckboxItems', value).click()
})
})
...
selectItems(value1)
selectItems(value1, value2)
This may cause trouble with the Typescript compiler, it's an older technique pre-typescript.
There is also Rest parameters
selectItems(...theValues) {
for (const value of theValues) {
cy.contains('dropdownlocatorCheckboxItems', value).click()
}
})
...
selectItems(value1)
selectItems(value1, value2)
Changing the parameter type to a string array is naïve.
You only shift the problem to the line before the function call
const selectItems(values: string[]) = {
...
})
const values = [value1, value2] // might as well just put these in the call params
selectItems(values)
You can change the signature of your function to an array and use .forEach to iterate through the array values.
const selectItems(values: string[]) = {
cy.get('dropdownlocator').click().then(() => {
// Add the forEach inside a .then to ensure it happens after you click the `dropdownlocator`
values.forEach((value) => {
cy.get('dropdownlocatorCheckboxItems').contains(value).click();
})
})
}
...
selectItems(['foo', 'bar', 'baz']);
selectItems(['foo']);
Related
I have a Store which will be provided to the component. In this Store file, there are several getter function. But I find only this getter function will be executed three times since this.rawMonthlyImpacts will be only changed once when the api get response from backend. I am so confused because other getter function in this file will be only executed once. During every execution, this.rawMonthlyImpacts is always same. Because this function is time-consuming, so I want to figure out why this happens. Hope you can give me some advice. Thanks!
get Impacts(){
const monthlyImpacts = new Map<string, Map<string, number>>();
if (this.rawMonthlyImpacts) {
this.rawMonthlyImpacts.forEach((impact) => {
if (impact.Impact > 0) {
const month = TimeConversion.fromTimestampToMonthString(impact.Month);
const tenantId = impact.TenantId;
const tenantImpact = impact.Impact;
if (!monthlyImpacts.has(month)) {
const tenantList = new Map<string, number>();
monthlyImpacts.set(month, tenantList.set(tenantId, tenantImpact));
} else {
const tenantWithImpactMap = monthlyImpacts.get(month);
if (!tenantWithImpactMap.has(tenantId)) {
tenantWithImpactMap.set(tenantId, tenantImpact);
} else {
tenantWithImpactMap.set(tenantId, tenantWithImpactMap.get(tenantId) + tenantImpact);
}
monthlyImpacts.set(month, tenantWithImpactMap);
}
}
});
}
return monthlyImpacts;
},
Update: I have find that there are other two functions use this.Impacts. If I remove these two functions, the getter function will only be executed only once. I think the getter function uses the cache to store data, so once the data is calculated for the first time, subsequent calls to the getter function should not be re-executed, only the value in the cache needs to be retrieved. So I am very confused about why this getter function will be executed 3 times.
getImpactedTenants(month: string): string[] {
return Array.from(this.Impacts.get(month).keys());
},
get overallMonthlyImpactedTenants(): Map<string, number> {
return new Map<string, number>(
Array.from(this.Impacts)?.map((monthEntries) => {
const month = monthEntries[0];
const impactedTenants = monthEntries[1].size;
return [month, impactedTenants];
})
);
}
Hard to tell what exactly is happening without more context, but remember that with a get function, every single time you reference that property (.Impacts in this case) the get function will be called.
Assuming that each impact stored in this.rawMonthlyImpacts which you loop through is an instance of the class with this getter, then as far as I'm aware, you are calling the get function each time you reference impact.Impacts, such as in the conditional:
if (impact.Impact > 0) {
I might be way off though; I'm unfamiliar with React and so my answer is based only on my experience with vanilla JS.
I made an input that let me filter a table of softwares.
<input type="text" id="softwares-search" class="form-control" aria-label="Input de recherche" aria-describedby="softwares-search">
Then in javascript my filter work well if I console.log(....)
But when I replace it with a return, nothing is returned. I think it is due to my var affectation through the event listener :
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
return softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
var results = softwaresSearch.addEventListener('keyup', maxwell)
console.log(results);
}
Thank all
EDIT 1 :
I was so angry, so blind, I had S#!t in my eyes, no need to use a global :(
const softwaresSearch = document.getElementById('softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', (e) => {
search = document.getElementById('softwares-search').value;
var filtredSoftwares = softwares.filter(e => e.name.includes(search) || e.description.includes(search) );
renderTable(filtredSoftwares);
});
}
const renderTable = (softwares) => {
Object.values(softwares).forEach(value=>{
console.log(value);
});
// Todo build HTML table
}
Instead of returning I think you just need to replace the current array like this
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
softwares = softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
And results is not needed:
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', maxwell)
}
As far as I know, softwareSearch.addEventListener won't return anything, since that is an event listener, and does not return any value. It simply executes the function passed in the 2nd parameter. You could try doing this instead
softwaresSearch.addEventListener('keyup', () => {
var results = maxwell();
console.log(results);
});
What this would do is that, it would call your maxwell function when the keyup event, since that is what it looks you are trying to do.
Please share all relevant code before posting a question, this code includes the variable "softwares" that exist outside what is visible to us.
Additionally, there are some issues with your code.
I don't understand your naming of the maxwell function. You should name functions as verbs, not anything else. A function is a machine that is doing something, and possibly returning something. It should be named to what it is doing.
On the second line, you say "search = ... ", but you didn't declare it as a variable.
You are returning something based on a value that isn't validated ('search' can be either undefined or a string value in this case), hence, your return will most likely just return undefined and not any value at all.
Your function can possibly not return anything at all since you are returning something within your if-statement. You can use a closure to always return something.
I would also suggest passing a search string as a variable to your function that should return a list based on the search query. Getting in the habit of short, concise functions with expected inputs/outputs, will make your code more readable and less error-prone and less likely to produce unwanted side-effects.
I don't know the rest of your code, but I don't recommend assigning variables in the global scope. Your "maxwell", "softwareSearch" variables both exist in the global space, unless you have wrapped them in another function block already (such as jquerys $(document).ready(() => { ...everything here is scoped })
You are getting the same element in two different places in your code.
Here is an updated code sample, but I can't test it since I don't know the rest of your code.
/*
* Executing the whole thing in this IIFE will make all variables declared inside here scoped to this block only,
* thus they can't interfere with other code you may write
*/
(() => {
const listOfSoftwares = softwares; // --- get your softwares variable here somehow, I don't know where "software" comes from.
// input element
const search = document.querySelector('#softwares-search');
/**
* Filter search results
* #param {string} query Search query
* #returns {Array} The results array
*/
const filterSoftwareSearchResults = (query) => {
let results = [];
results = listOfSoftwares.filter(software => software.description.includes(query) || software.title.includes(query))
// Verify
console.log(results);
// Should return array of results, even if empty
return results;
}
if (search) {
search.addEventListener('keyup', () => {
filterSoftwareSearchResults(search.value)
})
}
})()
The addEventListener function always returns undefined, so your results variable is undefined.
Returning from the callback function (maxwell) is also of no use.
You either need to do something with the data inside of your callback, or maybe pass the data to a global variable.
Can someone explain the part in the exports section, I seem to lost and stuck for a while. Starting from importPromise. It seems like there's a lot going on, such as arrow functions and map method. I can't see where the data flows from where to where.
const keystone = require('keystone');
const PostCategory = keystone.list('PostCategory');
const Post = keystone.list('Post');
const importData = [
{ name: 'A draft post', category: 'Keystone JS' },
...
];
exports = function (done) {
const importPromise = importData.map(({ name, category }) => createPost({ name, category }));
importPromise.then(() => done()).catch(done);
};
const categories = {};
const createPost = ({ name, category }) => {
let postCategory = new PostCategory.model({ category });
if (categories[category]) {
postCategory = categories[category];
}
categories[category] = postCategory;
const post = new Post.model({ name });
post.category = postCategory._id.toString();
return Promise.all([
post.save(),
postCategory.save()
]);
}
Quite some ES6 magic involved :)
const importPromise = importData.map(({ name, category }) => createPost({ name, category }));
importdata is an array. What the map function on an array does is to take every item of the array and apply a function to it, then return a new array with all of the items in the original array, but modified. map function
Instead of writing .map(function(item) { ... } the preferred way of writing this in ES6 is with a fat arrow function, i.e. .map((item) => ...
The third bit of magic is called destructuring assignment. What it does is it takes an object in this case, and assigns the obj.name and obj.category to two new variables name and category. We can use those variables within our function as if we had called the function with two separate arguments.
Now remember our map function dictates we write a function that takes an array item as a parameter, and returns the modified item. So what we end up with is a map function looping through the arguments of importData, taking name and category of each item and calling another function createPost with them. The result of createPost is the new value of the item, and it all gets appended to an array of the same size as the old one, with the modified items.
importPromise.then(() => done()).catch(done);
createPost creates a promise out of each item. You can read more about Promise here. The .then method on a Promise takes functions as its argument; to be called when the promise returns (either with success or an error). The () => done() is simply a function in fat arrow syntax that takes no arguments and calls the done function. .catch takes a function as well (done is a function), and is executed when the promise returns an error. NB. this way the done function is called both on success and on error!
--and no, this code won't work because what we're creating on the first line with importPromise is not actually a promise, but an array of promises!
Good luck with reading up, and as Beri suggests it might be worthwhile translating the code to es5 to follow along.
I don't know much about KeystoneJS. Anyway, here are my two cents:
const importData = [
{ name: 'A draft post', category: 'Keystone JS' },
// ...
];
importData is an Array which holds a bunch of Object instances, each having a name and category key with String values. For me, it appears this is some "mock data", which is simply put there for testing purposes.
I shifted the next parts, because it makes the code more understandable.
This part:
const categories = {};
Looks to me like the person who wrote it tried to implement some form of "caching". The categories constant is a mere "container" to store posts in so they can be reused later instead of being recreated. The createPost function reveals the purpose of it if you read through it.
const createPost = ({ name, category }) => {
let postCategory = new PostCategory.model({ category });
if (categories[category]) {
postCategory = categories[category];
}
categories[category] = postCategory;
const post = new Post.model({ name });
post.category = postCategory._id.toString();
return Promise.all([
post.save(),
postCategory.save()
]);
}
The first if seems to be there to make use of the "caching" construct (const category), but the way it does it is a bit confusing. Here's how I'd refactor it:
const createPost = ({ name, category }) => {
if (!categories[category]) {
categories[category] = new PostCategory.model({ category });;
}
const post = new Post.model({ name });
post.category = categories[category]._id.toString();
return Promise.all([
post.save(),
categories[category].save()
]);
}
Finally for the exports part:
The module exports a function which awaits a callback as argument (done). It then tries to create a Promise from all the "posts" of the mock data (and - to my understanding - fails), by mapping the createPost function over it. The reason I think it fails is because Array.prototype.map doesn't return a Promise, it returns a new Array instance which doesn't have a then method (see next line). Instead of calling then, it should be a Promise.all again. When that final Promise succeeds (or fails), the callback is called with the result.
exports = function (done) {
const importPromise = importData.map(({ name, category }) => createPost({ name, category }));
importPromise.then(() => done()).catch(done);
};
Again, I'd rewrite it this way:
exports = function (done) {
Promise.all(importData.map(createPost)).then(done).catch(done);
};
Or just return the final Promise and get rid of the done callback altogether.
https://babeljs.io/repl
You can use this tool to translate.
I'm working on an emulator. The task at hand is an incoming request on a certain endpoint. The request may contain 1-4 options in the req.body.options. The basic design idea is that an object contains the options and the corresponding method calls (as some sort of a sub-router).
let dataActions = {
option1: optionMethod(param1, param2),
option2: optionMethod2(param1, param2),
option3: optionMethod3(params),
option4: optionMethod4(params)
}
for (key in req.body.options) {
...
}
The for...in should fire the methods (decoupled in other files) when it finds matching in the request with the dataActions keys. Is there a semantical way, or a detailed design pattern to make this work?
The problem is that you already fire the methods yourself.
let dataActions = {
option1: optionMethod(param1, param2) // <-- this is a function call
}
Doing it this way you assign the result of optionMethod() to option1. The above is effectively shorthand for
let dataActions = {};
dataActions.option1 = optionMethod(param1, param2);
If that helps making it more obvious.
You don't want to call the methods immediately. You want to store them for later use. Either store them directly:
let dataActions = {
option1: optionMethod // <-- this is a function reference
}
...or store a function that calls them in some specific way:
let dataActions = {
option1: function () {
return optionMethod('some', 'parameters');
}
}
now you can use them at a separate time, for example like this
Object.keys(dataActions).filter(a => a in req.body.options).forEach(a => {
var optionMethod = dataActions[a];
optionMethod();
});
I am trying to write a filter function that takes 2 parameters:
id type and the actual id value. Using these IDs, I want to filter an array of objects.For example, here I am trying to get a new array that only includes the values with the name of 'Mike'.
object:
var sample = [
{ name: 'Mike'},
{ name: 'John'}
];
filter function:
function filterById(obj, parameter, id) {
return obj.parameter == id;
}
this:
console.log(sample.filter(filterById(name, 'Mike')));
returns name is not defined.
Do I need to pass in the actual array as well? Is it possible to pass parameters into filter functions at all?
You would need to pass the "parameter" as a string too, and use the square bracket notation, and for this all to work your filterById function would itself have to return a function which matches the function used by Array.prototype.filter:
var sample = [
{ name: 'Mike'},
{ name: 'John'}
];
function filterById(parameter, id) {
return function(obj){
return obj[parameter] == id;
}
}
console.log(sample.filter(filterById('name', 'Mike')));
You don't have to invoke the function by yourself – it is a high-order function, so you have to provide only function. And here we come to the problem – you want to pass arguments there, but you can't!
So, there are few approaches. The first one is just to return another function, which will keep data in closure:
function filterById(parameter, id) {
return function(item) {
return item[parameter] == id;
}
}
The second option is to create another function via .bind, which is close to the idea of partial application. It will create new function with pre-defined parameters. They are always first, so you have to move actual item definition to the last position:
function filterById(parameter, id, item) {
return item[parameter] === id;
}
// we can create function for the future reference:
const filterByMike = filterById.bind(null, 'name', 'Mike');
sample.filter(filterByMike);
It's hard to say what is better, but I'd personally prefer the second approach.