Get function arguments from outside without executing this function in javascript - javascript

Is it possible to receive the arguments list of the function without executing it somehow? I can't find a normal solution unfortunately.
I'm working on some Dependency Injection system and I'd like to know arguments naming in the function (service) before I create the instance of it. Because I want to create the Dependencies first and then pass them into the function (service).
for ex:
function MyServiceA(myServiceB) {
say() {
...code.........
myServiceB.beep();
}
}
function MyServiceB() {
beep() {
...code.........
}
}
function Creator() {
// Is it possible to receive it somehow? this is the question
const argumentsList = MyServiceA
// iterate list and instantiate the ServiceB dependency and inject in into the MyServiceA
argumentsList.forEach(dependency => {
const dependencyInstance = new dependency()
new MyServiceA(dependencyInstance )
})
}
Thanks for any help!

You could turn the function into a string, and then parse that string to find the names of the arguments. AngularJs did something like this for its dependency injection. Be aware that if the code gets minified, the names will change and this can break your code.
function needsA(a) {
console.log("in needsA");
a();
}
function needsB(b) {
console.log("in needsB");
b();
}
function needsAandB(a, b) {
console.log("in needsAandB");
a();
b();
}
const dependencyLookup = {
a: () => console.log('in A'),
b: () => console.log('in B'),
}
function injector(fxn) {
const argNames = fxn.toString()
.match(/^function\s*[^\(]*\(([^\)]*)\)/)[1]
.split(',');
const dependencies = argNames
.map(str => dependencyLookup[str.trim()]);
fxn.apply(null, dependencies);
}
injector(needsA);
injector(needsB);
injector(needsAandB);
A useful article on angularjs's dependency injection: https://teropa.info/blog/2014/06/04/angularjs-dependency-injection-from-the-inside-out.html

Related

Attempting to mock child method, fails if parent module imports it using object destructuring

I was doing some basic jest unit testing in attempt to learn it more.
I have this issue I do not know how to explain
This file has the child function add
// FileB.js
const add = (a, b) => {
return a + b;
}
module.exports = {
add,
};
This file has the parent function addTen
// FileA.js
const { add } = require('./FileB');
const addTen = num => {
return add(10, num);
}
module.exports = {
addTen,
};
this is my test file, where I am trying to either check <mockedFunction/spy>.mock.calls or do toHaveBeenCalledWith to see if the inner child method, add, is being passed in 10,10 when i call addTen(10);
This file is not used in any real env, its simply me trying to learn jest + unit testing more.
// randomTest.js
const { addTen } = require('../src/FileA');
const fileB = require('../src/FileB');
describe('temp', () => {
it('temp', () => {
const addSpy = jest.spyOn(fileB, 'add');
addTen(10);
console.log(addSpy.mock.calls);
expect(addSpy).toHaveBeenCalledWith(10,10)
});
});
Now for the issue, the test fails, saying add was never called, or nothing passed in. I logged the add function within FileB
However, if I modify FileA in this way, by importing entore module instead of destructuring, the test passes and I cna log out the functions and everything and see it works
This is what works
// FileA.js
const fileB = require('./FileB');
const addTen = num => {
return fileB.add(10, num);
}
module.exports = {
addTen,
};
Why does this slight change work?

Jest test functions that define variables in it

I want to write some Jest tests for JavaScript functions in a .mjs file. Within these functions, variables are defined by calling other functions. For example:
export function getCurrentVideo() {
var currentVideo = VideoList.GetCurrentVideo()
console.log(currentVideo);
return "This is the video:" + currentVideo
}
In this case I will receive undefined, right? Because VideoList.GetCurrentVideo can't be reached.
Test will be something like:
const getCurrentVideo = import('../').getCurrentVideo;
describe('getCurrentVideo', () => {
test('if getCurrentVideo will return "This is the video:" + currentVideo', () => {
expect(getCurrentVideo).toBe('This is the video:" + currentVideo');
});
});
I know you can add parameters to the function, but that will mean that I have to re-write the functions just for test purposes. It isn't my code, it's a huge project where the owner wants some tests for it.
You can mock function, assuming they work correctly (and test them separately):
const getCurrentVideo = import('../').getCurrentVideo;
describe('getCurrentVideo', () => {
it('if getCurrentVideo will return "This is the video:" + currentVideo', () => {
const currentVideo = 'testCurrentVideo';
window.VideoList = {
GetCurrentVideo: jest.fn(() => currentVideo)
}
expect(getCurrentVideo).toBe('This is the video:' + currentVideo);
});
});
Or you can provide a full context of VideoList, so that the two function are tested together.

Cucumber JS: How to export/update global variable outside Given/When/Then step?

I try to update/export global variable (firstString) to use and validate it in 'Then' step.
How do I export it correctly? When I'm doing it this way, the firstString is undefined.
It works only when I export/import it inside steps. How can I update it globally and use it in 'Then' file?
helpers.js:
let firstString;
given.js:
let { firstString } = require('./helpers')
Given('I have first {string}', function (stringValue) {
return stringRequest(stringValue).then(response => {
firstString = response
});
});
module.exports = { firstString }
then.js:
firstString = require('./helpers').firstString
Then('blablabla {string}', function (stringType) {
console.log(firstString)
});
If I am correct in understanding what you want to do you are wanting to store data across steps. Todo that you will want to use the world instance that cucumber provides for you. You can access the world instance in steps via the this keyword.
So what you would do is
Given('I have first {string}', function (stringValue) {
return stringRequest(stringValue).then(response => {
this.firstString = response
});
});
Then('blablabla {string}', function (stringType) {
console.log(this.firstString)
});
For more information on the world instance check out https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/world.md

How can I expose a public api from this specific IIFE JS structure?

In AspNetZero/ABP, the default index.js for an entity looks like the following:
(function () {
$(function () {
var _$formulationFrequenciesTable = $('#FormulationFrequenciesTable');
var _formulationFrequenciesService = abp.services.app.formulationFrequencies;
function additionalFilter() {
return {
nameFilter: // something
prop2: // something else
};
}
// more code
});
})();
We are using Telerik's AspNetCore Kendo on the Razor, so we define the grid like so:
Html.Kendo().Grid<PatientManagement.Formulations.Dtos.GetFormulationFrequencyForViewDto>()
.Name("gridFormulationFrequencies")
.DataSource(d =>
{
d.Custom()
.Type("aspnetmvc-ajax")
.ServerFiltering(true)
.ServerPaging(true)
.ServerSorting(true)
.Schema(s => s.Aggregates("aggregateResults").Data("data").Errors("errors").Total("total").Model(model => model.Id(m => m.Id)))
.Transport(t => t.Read(read => read.Action("FormulationFrequency_Read", "FormulationFrequencies", new { area = "App" }).Data("additionalData").Type(HttpVerbs.Get)));
})
.Deferred(true).Render();
How can I make additionalData "public" so that I can use it in the grid definition? I understand that the first line of the js is IIFE, and the second is short hand js for jQuery(document).ready().
I've been having issues trying to define a public API because everything is defined within the scope of the document ready, and IIFE examples I've seen don't include this curveball.
One way would be to assign a reference to the function to a window property.
If you have to do this often you could have a global object of your own in window namespace and assign as a property to that object instead
(function () {
$(function () {
var _$formulationFrequenciesTable = $('#FormulationFrequenciesTable');
var _formulationFrequenciesService = abp.services.app.formulationFrequencies;
function additionalFilter() {
return {
nameFilter: // something
prop2: // something else
};
}
// add to global window namespace
window.additionalFilter = additionalFilter;
// more code
});
})();

Confusion on how to work with module pattern

I am confused on how to work with module pattern (and design patterns in general) in JavaScript.
I already wrote some functioning code in my application using module pattern that does what I want to, but it doesn't seem to be very modular to me, and I keep having this feeling that I am doing it wrong. I didn't manage to find any concrete and complete application example with any design pattern.
Here is how I work with it :
Let's say I have forms in my application that I'll use for different modules (post a thread, reply to a thread, comment the guests book), with some JavaScript I'll give users some functionalities, as such as popping a smiley bubble and handling insertion of them in my forms, sending data posts to my server code to return the HTML code in order to add the message without reloading the page, I'll do something like that:
let Form = function (selector_form, selector_textarea, selector_emoticonsButton, selector_postButton) {
let form, textarea, emoticonsButton, postButton;
let emoticonsBubble = new EmoticonsBubble()
return {
selectors: function () {
return {
form: function () { return selector_form },
sendButton: function () { return selector_sendButton }
}
}
setElements: function (obj) {
form = $(obj).get(0);
textarea = $(form).find(selector_textarea).get(0);
emoticonsButton = $(form).find(emoticonsButton).get(0);
postButton = $(form).find(selector_postButton).get(0);
emoticonsBubble.setElements(form, emoticonsButton);
},
get: function () {
return {
form: function () { return form },
//...
emoticonsBubble: function () { return emoticonsBubble }
}
},
post: function (moduleId, callback) {
$.ajax({
//parameters
}).done(function (data) {
callback(data);
});
}
}
}
let EmoticonsBubble = function () {
let thisContainerToAppendTo, thisTextarea;
return {
setElements: function (container, textarea) {
thisContainerToAppendTo = container;
thisTextarea = textarea;
},
pop: function () {
this.ajax().pop(function (data) {
$(thisContainerToAppendTo).append(data);
});
}
insert: function (emoticon) {
$(thisTextarea).append(emoticon);
},
ajax: function () {
return {
pop: function (callback) {
$.ajax({
//parameters
}).done(function (data) {
callback(data);
});
}
}
}
}
}
// Events part
let form = new Form('#threadForm', '.textarea', 'button[name="emoticons"]', 'button[name="send"]');
let emoticonsBubble = form.get().emoticonsBubble();
$(form.selectors().form()).on('click', function (e) {
form.setElements(this);
});
$(form.selectors().sendButton()).on('click', function (e) {
let moduleId = // retrieve module id, if it belongs to guests book, thread creation module or reply module
form.post(moduleId, function (data) {
// append data to something
});
});
// etc for emoticons handling
The fact that I have to rewrite the event part for every different form I have in my application while keeping everything the same but variables name, annoys me a lot.
Could you guys tell me how you would handle those functionalities and what may be wrong with my way of coding?
The Module Pattern is about keeping units of code from colliding with other scopes (usually the Global scope).
As we know, in JavaScript, variables defined with:
let and const are scoped to their parent block
var are scoped to their containing function (or Global if not in a
function)
So, if you were to take your Form function:
let Form = function (x,y,z) {
let form, textarea, emoticonsButton, postButton;
let emoticonsBubble = new EmoticonsBubble()
return {
. . .
}
setElements: function (obj) {
. . .
},
get: function () {
. . .
},
post: function (moduleId, callback) {
. . .
}
}
}
The variable Form is Global because there is no containing block. This is a problem because what if there is already another Global called Form (which there very well could be because of the generic nature of the word "Form"). So, this code doesn't cut off your code from being exposed. To use the Module Pattern on it, we'd wrap it with an IIFE (Immediately Invoked Function Expression) and within that IIFE, we'd create a custom namespace in the Global scope that we're sure doesn't exist (thereby avoiding name collisions):
(function(){
// This is going to be exposed as publicly available via the module namespace
function Form(x,y,z) {
. . .
}
// This will remain private within the module
function helper(){
}
// **********************************************************************
let temp = {}; // Create a temporary object to bind only the public API
temp.Form = Form; // Bind the public members to the object
// Expose the module to the Global scope by creating a custom namespace
// and mapping the temp object to it
window.myCustomAPI = temp;
})();
// Now, outside of the module (in some higher scope), your public portions
// of the Module are accessible:
let myForm = new myCustomAPI.Form(arg, arg, arg);
The repetition in your code basically comes from the selection of elements and their helpers, and that can easily be abstracted into a function:
function Elements(selectors, children, options) {
let elements = { ...children };
return {
selectors,
elements,
setElements(obj) {
for(const [name, selector] of Object.entries(selectors))
elements[name] = $(obj).find(selector).get(0);
for(const child of Object.values(child))
child.parent && child.parent(this, obj);
},
...options
}
}
That can then be used as:
function Form(form, textarea, emoticonsButton, postButton) {
const emoticonsBubble = EmoticonsBubble();
return Elements({ form, textarea, emoticonButtons }, { emoticonsBubble }, {
post() {
//...
}
});
}
function EmoticonsBubble() {
return Elements({ /*...*/ }, {}, {
parent(parent, obj) {
this.setElements(parent);
}
});
}
But you are basically reinventing a lot of wheels here, have you thought about using one of the MVCs that are out there (React, Vue, ...) ?
Ok the boilerplate for some common tasks that you have in the event part is driving you crazy right ?
So checking your code you can fix them in many ways.
A. Encapsulate your code in real modules I mean this.
const Form = (function(/*receive here dependencies as arguments */){
// your code module goes here
})(/*inject dependencies here to module*/);
B. You can create a event pattern module, to drive your internal and externals events for module.
C. You know what are the listener that the module needs , so apply them into your module.
That way should be more reusable than now

Categories