I'm a new developper in Node.js (coming from Python), and I'm quite surprised to see that there are no decorators in Javascript. I would like to use it, however, because it greatly simplifies the code.
After some research, I found an ES6 specification in stage 2 for this (https://github.com/tc39/proposal-decorators), but it is apparently not supported in Node.js. I also found this in TypeScript, but their function is limited (only in classes).
So my question is: Is there a way to have decorators similar to those of Python with Node.js, and if not, what features can be used as a substitute ?
Thank you in advance for your answers !
According to Wikipedia decorators in python are just syntactic sugar for a function call, which takes the original class/function and stores the returned value under the variable containing the class. Thus the equivalent js would be:
const Decorated = decorator(class ToBeDecorated {
/*...*/
});
const decoratedFunction = decorateFunction(function decorated() {
/*...*/
});
// Some sample decorator implementations:
const decorator = Parent => class WithDuck extends Parent {
quack() { }
};
const decorateFunction = fn => (...args) => {
console.log(`${fn.name} called with`, ...args);
return fn(...args);
};
#jonas Wilms 's answer is correct. I wanted to post this here to expand on a specific implementation technique I used. My use case was with Koa, using async/await functions.
First, I created my decorator:
export function Cocoon( { param=[] }, callback )
{
//Return a function from the decorator
return async (ctx) => {
//Do a thing with Param
const params = { usr: param }
return await callback( ctx, params )
}
}
Using the decorator to annotate a function:
export const list = Cocoon({
param: [5]
},
async (ctx, { usr }) => {
//Function body
ctx.response.body = usr
}
)
Now I can export 'list' to Koa and it'll be called as expected.
Related
I'm build a store, and want to make action generator or others with a plugin.
On there met a problem. Because of this.
That is impossible thing on Nuxt & Vuex?
I posted plugin's source code ( with some black box )
My final goal is generateActionList with configuration object
export default ({ app }, inject) => {
const callAxios = (/* params */) => { /* some codes */ }
const generateAction = (config) => {
return (context, payload) => callAxios(/* params */)
}
const generateActionList = (config) => { // <= it's my Goal
const actions = {}
for (const [key, config] of Object.entries(conf)) {
actions[key] = generateAction(config)
}
return actions
}
inject('storeUtil', {
callAxios,
generateAction,
generateActionList
})
}
if have some solution, please talk to me.
i saw, 'this' is only lexically available on document.
ps) https://nuxtjs.org/guide/vuex-store/
Store is a module, not an instance of a class, so no this.
Instead of ...this.$storeUtil, get it from Vue directly.
Vue.prototype.$storeUtil
It might also be available at Vue.$storeUtil. If not, you could always init it there in storeUtil.
As an reference, this is how filters are reused outside of a component.
If it was never registered as prototype, you can just import it directly.
import storeUtil from '../storeUtil'
//...
[...storeUtil]
///...
I'm working on some JS package and I want to provide separated flow type definitions as index.js.flow because internal behavior is slightly different.
I have this function for create component definition
function defineComponent(name, createFunc);
createFunc is function which grabs elements and returns object containing particular user defined actions upon that component
so you can call defineComponent in this manner:
const loginForm = defineComponent("login form", () => {
...
...
return {
fillUsername: () => { ...doesn't matter what is return type... },
fillPassword: () => { ...doesn't matter what is return type... }
};
});
and those actions should be chainable, but I don't want to burden user with always mentioning return type within each user defined action. So final chain should look like this:
loginForm
.fillUsername()
.fillPassword()
So internally defineComponent will wrap each user defined action like this for chaining ability:
function defineComponent(..., createFunc) {
const actions = createFunc(...);
return actions.map(action => {
return (...args) => {
action(...args);
return actions;
}
})
}
I already tried this (my whole testing code):
type ComponentType<T> = $ObjMap<T, <V>((V) => any) => V => ComponentType<T>>;
declare function defineComponent<T>(
name: string,
createFunc: () => T
): ComponentType<T>;
const Component = defineComponent("foo", () => {
return {
fooAction: () => {},
barAction: () => {}
};
});
Component.fooAction().barAction()
I proceeds with flow's No errors! but flow is showing no errors also when I do something like
Component.fooAction.barAction()
And also VS Code provides no autocompletion above that :/
Thanks!
This is what I was able to come up with. The main idea is that we map the original value type V => mixed (i.e., a unary function) to V => T (i.e., same single argument but now it returns the action map T).
function chainable<T: { [actionName: string]: Function }>(
actions: T
): $ObjMap<T, <V>(V => mixed) => (V => T)> {
return Object.keys(actions).reduce((newActions, actionName) => {
newActions[actionName] = function(...args) {
actions[actionName](...args);
return this;
}
return newActions;
}, {});
}
Try Flow
NOTE: I don't think this is completely typesafe because this isn't really of type T. But, I don't think that Flow has the complexity to annotate the type of this in the newActions values. Additionally, this only works if all actions have unary functions for values. Unfortunately, Flow does not have the vocabulary to describe variadic generics (see https://github.com/microsoft/TypeScript/issues/5453 for information on what this means and how it could be used).
I'm having a pretty complex selectors structure in my project (some selectors may have up to 5 levels of nesting) so some of them are very hard to test with passing input state and I would like to mock input selectors instead. However I found that this is not really possible.
Here is the most simple example:
// selectors1.js
export const baseSelector = createSelector(...);
-
// selectors2.js
export const targetSelector = createSelector([selectors1.baseSelector], () => {...});
What I would like to have in my test suite:
beforeEach(() => {
jest.spyOn(selectors1, 'baseSelector').mockReturnValue('some value');
});
test('My test', () => {
expect(selectors2.targetSelector()).toEqual('some value');
});
But, this approach won't work as targetSelector is getting reference to selectors1.baseSelector during initialization of selectors2.js and mock is assigned to selectors1.baseSelector after it.
There are 2 working solutions I see now:
Mock entire selectors1.js module with jest.mock, however, it won't work if I'll need to change selectors1.baseSelector output for some specific cases
Wrap every dependency selectors like this:
export const targetSelector = createSelector([(state) => selectors1.baseSelector(state)], () => {...});
But I don't like this approach a lot for obvious reasons.
So, the question is next: is there any chance to mock Reselect selectors properly for unit testing?
The thing is that Reselect is based on the composition concept. So you create one selector from many others. What really you need to test is not the whole selector, but the last function which do the job. If not, the tests will duplicate each other, as if you have tests for selector1, and selector1 is used in selector2, then automatically you test both of them in selector2 tests.
In order to achieve:
less mocks
no need to specially mock result of composed selectors
no test duplication
test only the result function of the selector. It is accessible by selector.resultFunc.
So for example:
const selector2 = createSelector(selector1, (data) => ...);
// tests
const actual = selector2.resultFunc([returnOfSelector1Mock]);
const expected = [what we expect];
expect(actual).toEqual(expected)
Summary
Instead of testing the whole composition, and duplicating the same assertion, or mocking specific selectors outputs, we test the function which defines our selector, so the last argument in createSelector, accessible by resultFunc key.
You could achieve this by mocking the entire selectors1.js module, but also importing is inside test, in order to have access over the mocked functionality
Assuming your selectors1.js looks like
import { createSelector } from 'reselect';
// selector
const getFoo = state => state.foo;
// reselect function
export const baseSelector = createSelector(
[getFoo],
foo => foo
);
and selectors2.js looks like
import { createSelector } from 'reselect';
import selectors1 from './selectors1';
export const targetSelector = createSelector(
[selectors1.baseSelector],
foo => {
return foo.a;
}
);
Then you could write some test like
import { baseSelector } from './selectors1';
import { targetSelector } from './selectors2';
// This mocking call will be hoisted to the top (before import)
jest.mock('./selectors1', () => ({
baseSelector: jest.fn()
}));
describe('selectors', () => {
test('foo.a = 1', () => {
const state = {
foo: {
a: 1
}
};
baseSelector.mockReturnValue({ a: 1 });
expect(targetSelector(state)).toBe(1);
});
test('foo.a = 2', () => {
const state = {
foo: {
a: 1
}
};
baseSelector.mockReturnValue({ a: 2 });
expect(targetSelector(state)).toBe(2);
});
});
jest.mock function call will be hoisted to the top of the module to mock the selectors1.js module
When you import/require selectors1.js, you will get the mocked version of baseSelector which you can control its behaviour before running the test
For anyone trying to solve this using Typescript, this post is what finally worked for me:
https://dev.to/terabaud/testing-with-jest-and-typescript-the-tricky-parts-1gnc
My problem was that I was testing a module that called several different selectors in the process of creating a request, and the redux-mock-store state that I created wasn't visible to the selectors when the tests executed. I ended up skipping the mock store altogether, and instead I mocked the return data for the specific selectors that were called.
The process is this:
Import the selectors and register them as jest functions:
import { inputsSelector, outputsSelector } from "../store/selectors";
import { mockInputsData, mockOutputsData } from "../utils/test-data";
jest.mock("../store/selectors", () => ({
inputsSelector: jest.fn(),
outputsSelector: jest.fn(),
}));
Then use .mockImplementation along with he mocked helper from ts-jest\utils, which lets you wrap each selector and give custom return data to each one.
beforeEach(() => {
mocked(inputsSelector).mockImplementation(() => {
return mockInputsData;
});
mocked(outputsSelector).mockImplementation(() => {
return mockOutputsData;
});
});
If you need to override the default return value for a selector in a specific test, you can do that inside the test() definition like this:
test("returns empty list when output data is missing", () => {
mocked(outputsSelector).mockClear();
mocked(outputsSelector).mockImplementationOnce(() => {
return [];
});
// ... rest of your test code follows ...
});
I ran into the same problem. I ended up mocking the reselect createSelector with jest.mock to ignore all but the last argument (which is the core function you want to test) when it comes to testing. Overall this approach has served me well.
Problem I had
I had a circular dependency within my selector modules. Our code base is too large and we don't have the bandwidth to refactor them accordingly.
Why I used this approach?
Our code base has a lot of circular dependencies in the Selectors. And trying to rewrite and refactor them to not have circular dependencies are too much work. Therefore I chose to mock createSelector so that I don't have to spend time refactoring.
If your code base for selectors are clean and free of dependencies, definitely look into using reselect resultFunc. More documentation here: https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc
Code I used to mock the createSelector
// Mock the reselect
// This mocking call will be hoisted to the top (before import)
jest.mock('reselect', () => ({
createSelector: (...params) => params[params.length - 1]
}));
Then to access the created selector, I had something like this
const myFunc = TargetSelector.IsCurrentPhaseDraft;
The entire test suite code in action
// Mock the reselect
// This mocking call will be hoisted to the top (before import)
jest.mock('reselect', () => ({
createSelector: (...params) => params[params.length - 1]
}));
import * as TargetSelector from './TicketFormStateSelectors';
import { FILTER_VALUES } from '../../AppConstant';
describe('TicketFormStateSelectors.IsCurrentPhaseDraft', () => {
const myFunc = TargetSelector.IsCurrentPhaseDraft;
it('Yes Scenario', () => {
expect(myFunc(FILTER_VALUES.PHASE_DRAFT)).toEqual(true);
});
it('No Scenario', () => {
expect(myFunc(FILTER_VALUES.PHASE_CLOSED)).toEqual(false);
expect(myFunc('')).toEqual(false);
});
});
I am playing with stage-2 decorators proposal and I have found some problems with class decorators. In legacy decorator, the next example works because class decorators took as their only argument a target constructor, but with stage-2 proposal receives a object descriptor.
function log(Class) {
return (...args) => {
console.log(`Arguments: ${args}`);
return new Class(...args);
};
}
#log
class Bar {
constructor(name, age) { }
}
I have read the tc39 proposal-decorators and built-in decorators,but it did not help me so much. How can I make this example works with the new proposal decorator? is it possible to use built-in decorators with babel?
To try some stuff, I developed my own basic wrap decorator, it works as expected for methods, and I want to extend its functionality to class.
const wrap = f => {
return wrapped => {
const { kind } = wrapped;
if (kind === 'class') {
return f(wrapped);
}
const { descriptor } = wrapped;
const original = descriptor.value;
descriptor.value = f(original);
return { ...wrapped, descriptor };
};
};
export default wrap;
With this basic decorator, I can create for example this logger method decorator that works.
import wrap from './wrap';
const loggerFunction = f => {
const name = f.name;
return function test(...args) {
console.log(`starting ${name} with arguments ${args.join(', ')}`);
return f.call(this, ...args);
};
};
export default wrap(loggerFunction);
And I can use it by this way:
class Foo {
#logger
method(argument) {
return argument;
}
}
Your question is a bit old, but I think it is still actual so here is my answer:
TLDR: Use a finisher property to wrap your class when using non-legacy stage 2 decorators.
The stage 2 API is not thoroughly documented and some things and use cases are missing.
In order to achieve what you want, define a special finisher property on the object you are returning in your decorator.
Therefore, this legacy stage-1 decorator code:
function log(Class) {
return (...args) => {
console.log(`Arguments: ${args}`);
return new Class(...args);
};
}
#log
class Bar {
constructor(name, age) { }
}
Will be equivalent to this non-legacy stage-2 decorator code:
function log(descriptor) {
return {
...descriptor,
finisher: (Class) => (...args) => {
console.log(`Arguments: ${args}`);
return new Class(...args);
},
};
}
#log
class Bar {
constructor(name, age) {}
}
You can apply the same principle to your wrap function.
I've tried to search for answer to this problem for some time now and I failed. So I decided to give it a try here. If there is a such question already and I missed it, I'm sorry for duplicate.
Assume I have this javascript module 'myValidator.js', where are two functions and one function calls the other one.
export const validate = (value) => {
if (!value.something) {
return false
}
// other tests like that
return true
}
export const processValue = (value) => {
if (!validate(value)) {
return null
}
// do some stuff with value and return something
}
Test for this like this.
I want to test the validate function, whether is behaves correctly. And then I have the processValue function that invokes the first one and returns some value when validation is ok or null.
import * as myValidator from './myValidator'
describe('myValidator', () => {
describe('validate', () => {
it('should return false when something not defined', () => {
...
}
}
describe('processValue', () => {
it('should return something when value is valid', () => {
const validateMock = jest.spyOn(myValidator, 'validate')
validateMock.mockImplementation(() => true)
expect(validate('something')).toEqual('somethingProcessed')
}
it('should return null when validation fails', () => {
const validateMock = jest.spyOn(myValidator, 'validate')
validateMock.mockImplementation(() => false)
expect(validate('somethingElse')).toEqual(null)
}
}
}
Now, the problem is that this doesn't actually work as processValue() actually calls the function inside the module, because of the closure I suppose. So the function is not mocked as only the reference in exports is changed to jest mock, I guess.
I have found a solution for this and inside the module to use
if (!exports.validate(value))
That works for the tests. However we use Webpack (v4) to build the app, so it transforms those exports into its own structure and then when the application is started, the exports is not defined and the code fails.
What's best solution to test this?
Sure I can do it with providing some valid and invalid value, for this simple case that would work, however I believe it should be tested separately.
Or is it better to not mock functions and call it through to avoid the problem I have or is there some way how to achieve this with JavaScript modules?
I finally found the answer to this question. It's actually in the the Jest examples project on GitHub.
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* This file illustrates how to do a partial mock where a subset
* of a module's exports have been mocked and the rest
* keep their actual implementation.
*/
import defaultExport, {apple, strawberry} from '../fruit';
jest.mock('../fruit', () => {
const originalModule = jest.requireActual('../fruit');
const mockedModule = jest.genMockFromModule('../fruit');
//Mock the default export and named export 'apple'.
return {
...mockedModule,
...originalModule,
apple: 'mocked apple',
default: jest.fn(() => 'mocked fruit'),
};
});
it('does a partial mock', () => {
const defaultExportResult = defaultExport();
expect(defaultExportResult).toBe('mocked fruit');
expect(defaultExport).toHaveBeenCalled();
expect(apple).toBe('mocked apple');
expect(strawberry()).toBe('strawberry');
});