How to call a function from a nested object? - javascript

I have an object called obj that has a nested object comments and a function startMatch that returns an object like this:
var obj = {};
obj.comments = {
startMatch: function(matchStrings, isCaseSensitive) {
return {
subscribe: function(delegate) {
delegate('test')
const unsubscribe = () => {
console.log("unsubscribed");
};
}
};
}
};
var subscription = obj.comments.startMatch([], false).subscribe(function(result) {
console.log(result)
});
I would like to make this in such a way that I could call the unsubscribe function like this:
subscription.unsubscribe();
But I can't figure out how to do it without getting the unsubscribe undefined error.

Choose the simplest way:
var obj = {};
obj.comments = {
startMatch: function(matchStrings, isCaseSensitive) {
return {
subscribe: function(delegate) {
delegate('test');
return { unsubscribe: () => console.log("unsubscribed") }
}
};
}
};
var subscription = obj.comments.startMatch([], false).subscribe(function(result) {
console.log(result)
});
subscription.unsubscribe();

Related

How do these nested JavaScript functions work?

I have a question related to this code:
const somaHorasExtras = (salario, valorHorasExtras) => salario + valorHorasExtras;
const calculaDescontos = (salario, descontos) => salario - descontos;
const verifiqueSe = (valor) => {
const assercoes = {
ehExatamenteIgualA(esperado) {
if (valor !== esperado) {
// eslint-disable-next-line no-throw-literal
throw {};
}
},
};
return assercoes;
};
const teste = (titulo, funcaoDeTeste) => {
try {
funcaoDeTeste();
console.log(`${titulo} passou!`);
} catch {
console.error(`${titulo} não passou!!!`);
}
};
teste('somaHorasExtras', () => {
const esperado = 2500;
const retornado = somaHorasExtras(2000, 500);
verifiqueSe(retornado).ehExatamenteIgualA(esperado);
});
teste('calculaDesconto', () => {
const esperado = 2300;
const retornado = calculaDescontos(2500, 200);
verifiqueSe(retornado).ehExatamenteIgualA(esperado);
});
My question is related to the verifiqueSe function specifically. How does this function work? Does someone can explain how this function work in conjunction with the inner function ehExatamenteIgualA? What is the assercoes which is returned?
Thank you.
Your verifiqueSe(valor) function returns an object. You may find it a little confusing, since this syntax:
const foo = {
bar() {
//
}
};
is just a short syntax for object method:
const foo = {
bar: function () {
//
}
};
So in order to call the bar() function, you'd need to reach it through foo:
foo.bar();

Problem pushing a value inside an array located in an object key

Suppose I have the following function:
const createMenu = () => {
const obj = {
consumption: [],
};
return obj;
};
This is a function that, when called, returns the object
{ consumption: [] }
What I am trying to do is create a key inside that object that is a function that, when called with a string parameter, it pushes the string into the array inside the key 'consumption';
Here's my attempt:
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {obj.consumption.push(item); };
obj.order = order;
return obj;
};
The expected result is that, when calling that function inside the object with a string parameter,like this:
createMenu().order('pizza');
when I run:
console.log(createMenu().consumption);
my result is:
['pizza']
but it is not working. I would appreciate if anyone could help me with this.
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {
obj.consumption.push(item);
};
obj.order = order;
return obj;
};
createMenu().order('pizza');
console.log(createMenu().consumption);
You creating two instance of createMenu,
Your likely wanting to create 1
const menu = createMenu()
Also if you want to chain the functions, you will want to return the obj again inside order.
Below is an example..
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {
obj.consumption.push(item);
return obj;
};
obj.order = order;
return obj;
};
const menu = createMenu().order('pizza');
console.log(menu.consumption);
You have to store the object created by createMenu() to a variable then perform operations to that variable. The update below should work.
In your code, you've created a new object when called the createMenu() function at the console.log. Which is not what you are wanting.
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {
obj.consumption.push(item);
};
obj.order = order;
return obj;
};
const menu = createMenu();
menu.order('pizza');
menu.order('burger');
console.log(menu.consumption); // ["pizza", "burger"]
.as-console-wrapper{min-height: 100% !important; top: 0}
You may want to consider a class for this. Set up the array in the constructor, add a method to update the array when a new menu item is introduced, and have a final method that returns your desired object.
class CreateMenu {
constructor() {
this.consumption = [];
};
orderItem(item) {
this.consumption.push(item);
return this;
}
getList() {
return { consumption: this.consumption };
}
};
const menu = new CreateMenu();
const order = menu
.orderItem('pizza')
.orderItem('cheese sticks')
.getList();
console.log(order);

Promise.all does not await for then

First of all I am a newbie in nodejs, please be indulgent :)
I have a problem with some async functions. In my case, a first API call is made and then a sub call is made on every item returned. this is my code after the first call is made :
const countryStatesCalls: Promise<any>[] = [];
countries.forEach((country, index) => {
countries[index] = convertToCountry(country);
const countryStatesCall = (countryId: number) => {
return new Promise((resolve, reject) => {
const countryStatesList = getStateCountries(countryId);
if (countryStatesList) {
if (Object.prototype.toString.call(countryStatesList) !== '[object Array]') {
resolve([]);
} else {
resolve(countryStatesList);
}
} else {
reject([]);
}
});
};
countryStatesCalls.push(
countryStatesCall(countries[index].id).then(countryStates => {
countries[index].states = countryStates;
}).catch(countryStatesType => {
countries[index].states = countryStatesType;
})
);
});
await Promise.all(countryStatesCalls).then(_ => {
res.json(countries)
});
the following code
res.json(countries)
is supposed to be executed after all promises execution has been finished.
EDIT : the getStateCountries async function :
const getStateCountries = async (countryId: number, lang?: string) => {
const listState = await odoo.searchRead(
'res.country.state',
{
filters: [
{
field: 'country_id',
operator: '=',
value: countryId,
},
],
fields: fieldState,
},
{ lang }
);
if (!listState.length) {
return [];
} else {
listState.forEach((state, index) => {
listState[index] = convertToState(state);
});
return listState;
}
};
the main function is an express controller
I think my mistake may be obvious but can you help me please, I read many topics and I cannot see what am I doing wrong.
Thank you in advance
[SOLUTION]
As pointed out by comments, I was doing it the wrong way around, my call on promise called another promise: the solution was to write :
const countryStatesCalls: Promise<any>[] = [];
for (let index = 0; index < countries.length; index++) {
countries[index] = convertToCountry(countries[index]);
if (withSates) {
countryStatesCalls.push(
getStateCountries(countries[index].id)
);
} else {
countries[index].states = [];
}
}
if (withSates) {
await Promise.all(countryStatesCalls).then((countriesStates) => {
countries.forEach((country, index) => {
country.states = [];
for (const countryStates of countriesStates) {
if (countryStates.length
&& countryStates[0].country_id.code === country.id) {
countries[index].states = countryStates;
}
}
});
res.json(countries);
});
} else {
res.json(countries);
}
thank you everyone
I can't exactly tell why it doesn't work, but this looks a bit like the Promise constructor antipattern. The getStateCountries function is async and already creates a promise, so you need to await it or chain .then() to it. The new Promise is not necessary.
Also I'd recommend to avoid forEach+push, rather just use map, and get rid of the countryStatesCall helper:
const countryStatesCalls: Promise<any>[] = countries.map((country, index) => {
countries[index] = convertToCountry(country);
return getStateCountries(countries[index].id).then(countryStatesList => {
if (countryStatesList) {
if (Object.prototype.toString.call(countryStatesList) !== '[object Array]') {
return [];
} else {
return countryStatesList;
}
} else {
throw new Error("getStateCountries() resolved to nothing");
}
}).then(countryStates => {
countries[index].states = countryStates;
});
});
await Promise.all(countryStatesCalls).then(_ => {
res.json(countries)
});

How to mock localStorage that works with Object.keys(localStorage)?

I am able to mock the basic localStorage in jest test like this:
// localStorage.js
export default new class {
store = {};
setItem = (key, val) => (this.store[key] = val);
getItem = key => this.store[key];
removeItem = key => { delete this.store[key]; };
clear = () => (this.store = {});
}();
// xxx.test.js
import localStorage from './localStorage';
window.localStorage = localStorage;
However, it won't work for mocking something like Object.keys(localStorage), which is supposed to return the keys in the localStorage in an array. Please help.
Thank you.
You can use JavaScript Proxy object to achieve Object.keys() ability.
E.g.
index.js:
class FakeLocalStorage {
store = {};
setItem = (key, val) => (this.store[key] = val);
getItem = (key) => this.store[key];
removeItem = (key) => {
delete this.store[key];
};
clear = () => (this.store = {});
}
let fakeLocalStorage = new FakeLocalStorage();
fakeLocalStorage = new Proxy(fakeLocalStorage, {
ownKeys: (target) => {
return Object.keys(target.store);
},
getOwnPropertyDescriptor(k) {
return {
enumerable: true,
configurable: true,
};
},
});
(async function test() {
fakeLocalStorage.setItem('name', 'TS');
fakeLocalStorage.setItem('age', 23);
const keys = Object.keys(fakeLocalStorage);
console.log('keys:', keys);
})();
The execution results:
keys: [ 'name', 'age' ]
When you have Object.keys(localStorage) to iterate through all available keys you can use spying instead of mocking using the following approach. Make sure you call mockRestore afterwards (not mockReset), or other tests that use local storage may fail.
The tested code:
const someFunction = () => {
const localStorageKeys = Object.keys(localStorage)
console.log('localStorageKeys', localStorageKeys)
localStorage.removeItem('whatever')
}
The test:
describe('someFunction', () => {
it('should remove some item from the local storage', () => {
const _localStorage = {
foo: 'bar', fizz: 'buzz'
}
Object.setPrototypeOf(_localStorage, {
removeItem: jest.fn()
})
const mock = jest.spyOn(global, 'localStorage', 'get')
try {
mock.mockReturnValue(_localStorage)
someFunction()
expect(_localStorage.removeItem).toHaveBeenCalledTimes(1)
expect(_localStorage.removeItem).toHaveBeenCalledWith('whatever')
} finally {
mock.mockRestore()
}
})
})

Mocking chained methods

(I use Jest for testing)
For example, I have this function:
const find = () => {
return {
where: () => {
in: () => {}
}
};
};
and I call that in different place:
find('me').where('id').in(['123']);
How to mock and test calls in find(), where() and in()?
Here's a dirt simple mock interface:
const find = (findData) => {
const data = {
find: findData
};
const self = {
where: (whereData) => {
data.where = whereData;
return self;
},
in: (inData) => {
data.in = inData;
return self;
},
data
};
return self;
};
const res = find('me').where('id').in(['123']);
console.log(res.data);

Categories