how to test effects with filter of ngrx in Angular 4 5 with Jasmine and Marble - store

I am now facing a problem. not sure how to test if the action$ with filter operator.
I am also trying to follow the rules of https://github.com/vsavkin/testing_ngrx_effects/tree/309b84883c2709a34ab98b696398332d33c2104f
make it simple, I just set the filter if the length of array is 0 return true.
for example:
loadDatas$: Observable<Action> = this.actions$.ofType(LOAD_DATAS_ACTION).pipe(
withLatestFrom(this.store.select(getDatas), (action, datas) =>datas),
filter(data => !data.length),
switchMap(() => {
console.log(‘run api’);
return this.dataApi.find().pipe(
map((datas: Data[]) => new DatasLoadedAction(datas))
………
…..
so I try to write two test cases, one is
expect(effects.loadDatas$).toBeObservable(expected);
when filter return true.
but I don’t know how to test if the filter return false.
Do you have any suggestion for this ? thank a lot

You expect the effect to not return a new action, so you can compare it with an 'empty' observable:
const expected = cold('----');
expect(effects.loadDatas$).toBeObservable(expected);

Related

nodejs map return nothing when if statement true

so i have the following code
data.push(commands.map(
command => {
if (!command.devOnly) { return command.name; } // first condition
if (command.devOnly && message.author.id != '3251268789058714880') {} // second condition
},
).join('\n'));
if the second condition is true it returns null but then when I run console.log(data) it has a blank line for all the commands where the second condition is true.
Is there a way to stop the second condition from returning anything, and not leaving the blank line
.map() is a 1-for-1 transformation so the output array will have EXACTLY the same number of elements in it as the input array. If you don't return anything, that element in the array will have an undefined value (which is the return value when you don't actively return something).
To transform the array and eliminate some elements, you cannot use .map(). You can either do .filter().map() where you first filter out the items you don't want and then map the others or you can use a regular for loop and just push the items into output array that you want to keep using either a regular for loop iteration or a .reduce() or .forEach() iteration.
One example:
const results = commands.filter(command => !command.devOnly).map(command => command.name);
console.log(results);
const results = [];
for (let command of commands) {
if (!command.devOnly) results.push(command.name);
}
console.log(results);
Note, your second condition doesn't do anything at all in your code example so I wasn't sure how to account for that in these examples.
P.S. I've often wished Javascript had a .filterMap() feature that let you return undefined to leave that value out of the result - otherwise work like .map(). But, it doesn't have that feature built in. You could build your own.
Per your comments, you can filter on two conditions like this:
const results = commands
.filter(command => !command.devOnly || message.author.id === '3251268789058714880')
.map(command => command.name);
console.log(results);
you could use Array.reduce
commands.reduce((prev,command)=>{
if (!command.devOnly)
return [...prev,command.name]
// this if, is useless but shows that you can use more conditions.
if (command.devOnly && message.author.id != '3251268789058714880')
return prev
// add more conditions as you need here
return prev
},[])
another options could be by doing map then filter the undefined values, or use for-loop
like jfriend00 explained in his answer. the filterMap() he wished could be implemented with Array.reduce

JS filter or find an element in array where owner_name matches in react

I have got the two following arrays.
producers = { owner_name: "testing123, url: "http://test.com/", logo_svg: "http://test.com/logo.svg"}, { owner_name: "testing456, url: "http://test456.com/", logo_svg: "http://test456.com/logo.svg"}
result = { owner_name: "testing123, true, false}
I am trying to find a way to extract the logo_svg url from producers array when the owner_name match. I am very new to JS, so I'm not very good at it.
I manage to put this together but unclear on how I can get the logo_svg from that.
image={producers.filter((producer) => producer.owner_name === result.owner_name)}
try image = producers.find(p => p.owner_name == result.owner_name).logo_svg
but bear in mind that if the owner name does not exist in producers then find returns undefined and so you'll get an error so you can make the whole expression more robust by doing
obj = producers.find(p => p.owner_name === result.owner_name)
obj_url = obj ? obj.logo_svg || 'default_url_location'
image = obj_url
According to MDN, filter produces an array, so that is why you cannot get a valid variable inside your image.
You can try something like:
image={producers[producers.findIndex(el => el.owner_name === result.owner_name)]}
This will find the index of the item you are looking for and pass it immediately as the index in the producers array.

Combine Search and checkbox filters in Vue js

I have to filter a list using a Search input field and als some Checkboxes (filter on a category).
I have both functionalities working independently.
The Search field
computed: {
getfilteredData() {
return this.experiences.filter(experience =>
experience.name.toLowerCase().includes(this.search.toLowerCase()) ||
experience.category.toLowerCase().includes(this.search.toLowerCase()
)
)
}
},
The Checkboxes
computed: {
getfilteredData() {
if (!this.checkedCategories.length)
return this.experiences
return this.experiences.filter(experience =>
this.checkedCategories.includes(experience.category))
}
},
How do I combine those filters? So they are working simultaneously?
combining both filters in succession will filter both as an AND statement
getfilteredData() {
return this.experiences.filter(experience =>
experience.name.toLowerCase().includes(this.search.toLowerCase()) ||
experience.category.toLowerCase().includes(this.search.toLowerCase()
)
).filter(experience =>
// if there are no checkboxes checked. all values will pass otherwise the category must be included
!this.checkedCategories.length || this.checkedCategories.includes(experience.category)
)
}
otherwise, you could combine them in one filter with (firstCondition || secondCondition) with the same logic you use above.
I saw your other question that got closed Write my Javascript more cleaner in my Vue js search functionality
where I think you could rewrite your function like this
experience => {
let reg = new RegExp(this.search, 'gi')
return reg.test(`${experience.name} ${experience.category}`)
}
using g means that your string can be in any position, but you must reconstruct your regex on each test otherwise you can end up with issues found here
Why am I seeing inconsistent JavaScript logic behavior looping with an alert() vs. without it?
using i means it will ignore casing so you don't need to worry about using toLowerCase()
thus your filter can be written like this in one statement
experience => {
let reg = new RegExp(this.search, 'gi')
// search input matches AND the checkbox matches
return reg.test(`${experience.name} ${experience.category}`) && (!this.checkedCategories.length || this.checkedCategories.includes(experience.category))
// search input matches OR the checkbox matches
//return reg.test(`${experience.name} ${experience.category}`) || (!this.checkedCategories.length || this.checkedCategories.includes(experience.category))
}

how to check if any of given queries return any result in knex

I have two queries:
a) select id from ingredietns where name = my_param;
b) select word_id from synonyms where name = my_param;
Both return 0 or 1 row. I can also add limit 1 if needed (or in knex first()).
I can translate each into knex like this:
knex("ingredients").select('id').where('name', my_param) //do we need first()?
knex("synonyms").select('word_id').where('name', my_param) //do we need first()?
I need function called "ingredientGetOrCreate(my_param)". This function would
a) check if any of above queries return result
b) if any of these return, then return ingredients.id or synonyms.word_id - only one could be returned
c) if record doesn't eixst in any of tables, I need to do knex inesrt aand return newly added id from function
d) later I am not sure I also understand how to call this newly create function.
Function ingredientGetOrCreate would be used later as seperate function or in the following scenario (like "loop") that doesn't work for me either:
knex("products") // for each product
.select("id", "name")
.map(function (row) {
var descriptionSplitByCommas = row.desc.split(",");
Promise.all(descriptionSplitByCommas
.map(function (my_param) {
// here it comes - call method for each param and do insert
ingredientGetOrCreate(my_param)
.then(function (id_of_ingredient) {
knex('ingredients_products').insert({ id_of_ingredient });
});
...
I am stuck with knex and Promise queries because of asynchronouse part. Any clues, please?
I though I can somehow use Promise.all or Promise.some to call both queries.
P.S. This is my first day with nodejs, Promise and knex.
As far as I decode your question, it consists of two parts:
(1) You need to implement upsert logic (get-or-create logic).
(2) Your get part requires to query not a single table, but a pair of tables in specific order. Table names imply that this is some sort of aliasing engine inside of your application.
Let's start with (2). This could definitely be solved with two queries, just like you sense it.
function pick_name (rows)
{
if (! rows.length) return null
return rows[0].name
}
// you can sequence queries
function ingredient_get (name)
{
return knex('ingredients')
.select('id').where('name', name)
.then(pick_name)
.then(name =>
{
if (name) return name
return knex('synonyms')
.select('word_id').where('name', name)
.then(pick_name)
})
}
// or run em parallel
function ingredient_get (name)
{
var q_ingredients = knex('ingredients')
.select('id').where('name', name)
.then(pick_name)
var q_synonyms = knex('synonyms')
.select('word_id').where('name', name)
.then(pick_name)
return Promise.all([ q_ingredients, q_synonyms ])
.then(([name1, name2]) =>
{
return name1 || name2
})
}
Important notions here:
Both forms works well and return first occurence or JS' null.
First form optimizes count of queries to DB.
Second form optimizes answer time.
However, you can go deeper and use more SQL. There's a special tool for such task called COALESCE. You can consult your SQL documentation, here's COLASCE of PostgreSQL 9. The main idea of COALESCE is to return first non-NULL argument or NULL otherwise. So, you can leverage this to optimize both queries and answer time.
function ingredient_get (name)
{
// preparing but not executing both queries
var q_ingredients = knex('ingredients')
.select('id').where('name', name)
var q_synonyms = knex('synonyms')
.select('word_id').where('name', name)
// put them in COALESCE
return knex.raw('SELECT COALESCE(?, ?) AS name', [ q_ingredients, q_synonyms ])
.then(pick_name)
This solution guarantees single query and furthermore DB engine can optimize execution in any way it sees appropriate.
Now let's solve (1): We now got ingredient_get(name) which returns Promise<string | null>. We can use its output to activate create logic or return our value.
function ingredient_get_or_create (name, data)
{
return ingredient_get(name)
.then(name =>
{
if (name) return name
// …do your insert logic here
return knex('ingredients').insert({ name, ...data })
// guarantee homohenic output across get/create calls:
.then(() => name)
})
}
Now ingredient_get_or_create do your desired upsert logic.
UPD1:
We already got ingredient_get_or_create which returns Promise<name> in any scenario (both get or create).
a) If you need to do any specific logic after that you can just use then:
ingredient_get_or_create(…)
.then(() => knex('another_table').insert(…))
.then(/* another logic after all */)
In promise language that means «do that action (then) if previous was OK (ingredient_get_or_create)». In most of the cases that is what you need.
b) To implement for-loop in promises you got multiple different idioms:
// use some form of parallelism
var qs = [ 'name1', 'name2', 'name3' ]
.map(name =>
{
return ingredient_get_or_create(name, data)
})
var q = Promise.all(qs)
Please, note, that this is an agressive parallelism and you'll get maximum of parallel queries as your input array provides.
If it's not desired, you need to limit parallelism or even run tasks sequentially. Bluebird's Promise.map is a way to run map which analogous to example above but with concurrency option available. Consider the docs for details.
There's also Bluebird's Promise.mapSeries which conceptually is an analogue to for-loop but with promises. It's like map which runs sequentially. Look the docs for details.
Promise.mapSeries([ 'name1', 'name2', 'name3' ],
(name) => ingredient_get_or_create(name, data))
.then(/* logic after all mapSeries are OK */)
I believe the last is what you need.

Protractor compare string numbers

Today I've faced interesting problem of create test for pretty simple behavior: 'Most recent' sorting. All what test need to know:
Every item have ID
Previous ID is less then next in this case of sorting
Approach: writing ID in to attribute of item, getting that id from first item with getAttribute() and either way for second.
Problem: getAttribute() promise resulting with string value and Jasmine is not able to compare (from the box) string numbers.
I would like to find elegant way to compare them with toBeLessThan() instead of using chains of few .then() that will be finished with comparing that things.
Root of no-type-definition evil
Thanks guys <3
You can create a helper function to convert string number to actual number, which will make use of Promises:
function toNumber(promiseOrValue) {
// if it is not a promise, then convert a value
if (!protractor.promise.isPromise(promiseOrValue)) {
return parseInt(promiseOrValue, 10);
}
// if promise - convert result to number
return promiseOrValue.then(function (stringNumber) {
return parseInt(stringNumber, 10);
});
}
And then use the result with .toBeLessThan, etc:
expect(toNumber(itemId)).toBeLessThan(toNumber(anotherItemId));
I forgot of native nature of promises but tnx to Michael Radionov I've remembered what I want to do.
expect(first.then( r => Number(r) )).toBe(next.then( r => Number(r) ));
I guess this stroke looks simple.
UPDATE
ES6:
it('should test numbers', async function () {
let first = Number(await $('#first').getText());
let second = Number(await $('#second').getText());
expect(first).toBeGreaterThan(second);
})
One option to approach it with a custom jasmine matcher:
toBeSorted: function() {
return {
compare: function(actual) {
var expected = actual.slice().sort(function (a, b) {
return +a.localeCompare(+b);
});
return {
pass: jasmine.matchersUtil.equals(actual, expected)
};
}
};
},
Here the matcher takes an actual input array, integer-sort it and compare with the input array.
Usage:
expect(element.all(by.css(".myclass")).getAttribute("id")).toBeSorted();
Note that here we are calling getAttribute("id") on an ElementArrayFinder which would resolve into an array of id attribute values. expect() itself is patched to implicitly resolve promises.

Resources