spying on/mocking super class methods in es6 - javascript

The below are examples but get at the gist of my problem ...
super class:
class Parent {
constructor(a) {
this._a = a;
}
doSomething() { ... implementation... }
}
child-class:
class Child extends Parent {
constructor() {
super('a');
}
doSomethingElse() { return super.doSomething(); }
}
I am using these classes in Angular, so the Parent class is DI'ed into the factory which provides the Child class, something like this:
function ChildDeps(Parent) {
return class Child extends Parent {
... Child class implementation here ...
};
}
ChildDeps.$inject = ['Parent']
Naively, I first tried something like this inside a provide before each clause:
beforeEach(module($provide => {
parent = {
doSomething: jasmine.createSpy('parent.doSomething')
};
Parent = jasmine.createSpy('Parent').and.returnValue(parent);
$provide.value('Parent', Parent);
}));
But this did not work ... saying that spy 'Parent' was never called.
Currently using jasmine/karma for testing. How can mock/spy the super class so I can make expectations about what the Parent class' constructor is called with and that the super.doSomething function is called?
Thanks!

Related

How to call parent method inside a javascript child class

Following is the code for a vue component js file.
(assuming that the vue component js file is a Class)
export default { -----> this is the parent, it is a component & it doesn't have a name!
name: "mapping",
components: {},
props: ["data"],
data() {
},
methods: {
parentMethod() {} ---->> this is the parent method. I want to call this inside the Rect class
},
mounted() {
class Rect { -----> this is the child class,
constructor() {
this.parentMethod() // -> this is the parent method. how can I do this?
}
// call parent methods (i.e. component's) inside this class
//or something like this.
this.parentMethod() // -> this is the parent method. how can I do this?
}
}
As you can see I'm creating a class called Rect inside the mounted hook inside the vue js component class.
What I want is to call methods of the parent component inside this Rect class.
How can I achieve that?
UPDATE
I'm not extending the parent component class inside itself. I'm just defining a new class called Rect inside the parent component class.
So I don't think I can call super().
Not sure though!!
UPDATE
As I go through answers, I came to see that most of them suggests extending the class. But here the parent class doesn't have a name. It's just export default {} in vue.
& also I'm not sure whether I will be able to extend a parent inside itself to create a new class within inside itself.
NOTE
The requirement is to call the parent method from/inside a class which is the child of parent class (ie defined within the parent => defined within the parent body) Hope that makes sense!!
When you create a new class you change who this is inside that class. So you need to give that class a reference to its parent:
mounted() {
const parentRef = this; // reference to the parent instance
class Rect {
constructor() {
parentRef.parentMethod()
}
...
parentRef.parentMethod() // -> this is the parent method
}
}
Check extends and super keywords:
//Inheritance
//Parent class (superclass)
class Animal {
constructor(name) {
this._name = name;
this._behavior = 0;
}
get name() {
return this._name;
}
get behavior() {
return this._behavior;
}
incrementBehavior() {
this._behavior++;
}
}
//Child class (subclass)
class Cat extends Animal { // The extends keyword makes the methods of the animal class available inside the cat class.
constructor(name, usesLitter) {
super(name); // The super keyword calls the constructor of the parent class.
this._usesLitter = usesLitter;
}
get usesLitter() {
return this._usesLitter;
}
}
const bryceCat = new Cat('Bryce', true);
console.log(bryceCat.name); //Output: Bryce
console.log(bryceCat.usesLitter); //Output: true
In Vue you want to emit events from child components that are handled by the parent component rather than calling parent component methods directly from the child.
See https://v2.vuejs.org/v2/guide/components.html#Listening-to-Child-Components-Events

typescript: member variables from super class's constructor

The following code will alert undefined
class Parent {
field: string
constructor() {
alert(this.field)
}
}
class Child extends Parent {
field = 'child'
}
new Child() #=> undefined
whereas, the following alerts 'child' as expected
class Parent {
field: string
constructor() {
alert(this.field)
}
}
class Child extends Parent {
field = 'child'
constructor() {
// without referencing this.field before super(), this.field stays undefiend
this.field
super()
}
}
new Child() #=> 'child'
Is there any ways to accomplish the following conditions?
omit the whole Child's constructor declaration like the first example
grab the member variable in Child class?
What jumps to mind is:
class Parent {
constructor(public field: string) {
alert(this.field)
}
}
class Child extends Parent {
constructor() {
super('child');
}
}
new Child() #=> 'child'
This doesn't meet your conditions but I feel it is fairly compact.
Well you could defer the property access to a micortask:
class Parent {
field: string
constructor() {
Promise.resolve().then(() => {
alert(this.field)
};
}
}
But while that fullfills your conditions, it is ... still the wrong approach. Pass field as a constructor argument, as other answers show.
There's no way to accomplish your conditions, i'm pretty sure.
Grabbing the member variable in sub class happens after the base class constructor runs, and calling super() must be the first statement in a sub class constructor.
class Child extends Parent {
field = 'child';
}
// the above is equal to:
class Child extends Parent {
constructor(){ super(); this.field = 'child'; }
}
And this would cause error:
class Child extends Parent {
constructor(){
this.field = 'child'; // ERROR!
super();
}
}

How to decorate a Web Component class

I'm creating a #Component decorator that intercedes the constructor of a class to carry out some work after construction. As can be seen in the following code, the work is implemented in an init method.
export function Component (Cls) {
function Class (...args) {
let self = new Cls (...args); // (1)
init (self, ...args);
return self;
}
Class.prototype = Cls.prototype;
return Class;
}
When I test this code on a regular class all works fine. This is a working example:
class Base { ... }
#Component
class Core extends Base {
constructor () {
super (); // init is invoked
}
fx () { console.log ('Core.fx') }
fy () { console.log ('Core.fy') }
}
Nevertheless, when I try to decorate a web component a TypeError: Illegal constructor message is obtained.
#Component
class Core extends HTMLElement {
constructor () {
super ();
}
fx () { console.log ('Core.fx') }
fy () { console.log ('Core.fy') }
}
customElements.define ('x-core', Core);
let coreX = document.createElement ('x-core');
document.body.appendChild (coreX);
I realise the problem is that HTMLElement's do not support direct construction through new operator - see (1) on first listing - but I need a procedure to decorate constructor of any class even though they are custom elements.
Some Idea?
Working Settings: Chrome 68 ยท Babel 7.0.0-beta.51 with babel-plugin-transform-decorators-legacy
You can return a class to avoid direct new.
function Component(cls) {
class c extends cls {
constructor() {
super()
console.log(this)//init
}
}
return c
}

Keep my code DRY for `$onInit` and `$onChanges` methods

I have a piece of code that is repeated in almost all of my components:
import {deviceCommands} from "../../../core/data/Commands";
let _generalProperties = {
'deviceName': deviceCommands.NAME.key,
'deviceModel': deviceCommands.MODEL.key
};
export default class GeneralDeviceSettingsCtrl {
constructor($scope) {
let $ctrl = this;
$ctrl.$onChanges = function (changes) {
for(let prop in _generalProperties) {
$ctrl[prop] = $ctrl.test.vm.data[_generalProperties[prop]];
}
};
$ctrl.$onInit = function () {
for(let prop in _generalProperties) {
$scope.$watch(() => $ctrl.test.vm.data[_generalProperties[prop]],
(newValue, oldValue) => {
if (newValue !== oldValue) {
$ctrl[prop] = $ctrl.test.vm.data[_generalProperties[prop]];
}
});
}
};
}
}
The only thing that is different is the _generalProperties variable, which is specific to my view.
How can I do to keep it DRY?
Make a base class? use decorators?
I think there is plenty of different approaches to this but if you stick with classes and inheritance you can supply the _generalProperties to your parent constructor and reuse the whole implementation.
For example:
export class BaseSettingsComponent {
constructor(properties){
this._properties = properties;
}
$onInit(){ /* ... implement base component stuff*/ }
}
Then, on each of your pages that uses the same component logic you can extend the base class, provide the properties to the parent class and let the base class do the job.
import { BaseSettingsComponent } from '../common/base-settings-component.js';
let _generalProperties = {
'deviceName': deviceCommands.NAME.key,
'deviceModel': deviceCommands.MODEL.key
};
export class GeneralDeviceSettingsCtrl extends BaseSettingsComponent {
constructor(){
super(_generalProperties);
}
$onInit(){ super.$onInit(); /* ... implement base component stuff*/ }
}
The one thing important to keep in mind when using this approach is that if you need to implement $onInit or $onChanges on the child classes you have to call the super.$onInit() otherwise you lose the parent behavior due to an override.
Finally, for an even cleaner code, you can also discard the declaration of _generalProperties supplying it directly into the super constructor.
import { BaseSettingsComponent } from '../common/base-settings-component.js';
export class GeneralDeviceSettingsCtrl extends BaseSettingsComponent {
constructor(){
super({
'deviceName': deviceCommands.NAME.key,
'deviceModel': deviceCommands.MODEL.key
});
}
}

Dynamically inherit from instance of the class in JavaScript

I am kind of struggling with inheritance in JavaScript. Let's say I have a following class:
class Parent {
constructor({ name, someOtherStuff } = {}) {
this.name = name;
this.someOtherStuff = someOtherStuff;
}
someMethod() {
// ...
}
}
and I would like to create a decorator that would allow me to do following:
#parent({
name: 'foo',
someOtherStuff: 'bar'
})
class MyClass extends Component {
myMethod() {
// ...
}
}
const instance = new MyClass();
// Those tests must pass
expect(instance.someMethod).toBeFunction();
expect(instance.name).toEqual('foo');
expect(instance.someOtherStuff).toEqual('bar');
expect(instance.myMethod).toBeFunction();
expect(instance instanceof Parent).toBe(true);
expect(instance instanceof MyClass).toBe(true);
Is there a way to create such decorator? I tried multiple solutions, but none of them really satisfies all the tests.
const parent = (...args) => (Target) => {
// Target corresponds to MyClass
const parent = new Parent(...args);
// ...
};
lodash is allowed.
Why use decorators? You can just extend parent class
class MyClass extends Parent {
constructor() {
super({name: 'foo', someOtherStuff: 'bar'});
}
}
You can use decorators to create a new class that inherits, apply some mixins, and go from there. JS classes don't have mutliple inheritance, so you can't do this directly, but you can combine the two manually or create a proxy that will do what you want.
I've been using wrapper classes for a decorator-based DI library by returning a class like so:
static wrapClass(target, {hook = noop} = {}) {
return class wrapper extends target {
static get wrappedClass() {
return target;
}
constructor(...args) {
super(...Injector.fromParams(args).getDependencies(wrapper).concat(args));
}
}
}
The decorator is really returning a new constructor with closure over the original, but that's enough for most purposes.

Categories

Resources