Aurelia pass configuration to plugin or feature - javascript

I want to pass some configuration information into my aurelia feature but I am not sure how. I found no documentation on how do this in the aurelia docs.
My Feature
Main.js
.feature('aurelia-scrollbar', config => {
// I want to pass an object along this
config.foo = { bar: 'yay' }
})
Index.js
export function configure(config) {
config.globalResources('./scrollbar');
}
Scrollbar.js
import Scrollbar from 'smooth-scrollbar';
import 'smooth-scrollbar/dist/smooth-scrollbar.css!';
export class ScrollbarCustomAttribute {
static inject = [Element];
constructor(element) {
this.element = element;
}
attached() {
Scrollbar.init(this.element); // I want to use the passed configuration option here
}
}

The feature method (and plugin method) will accept your feature-specific configuration as a parameter:
main.js
let scrollbarConfig = { foo: 'bar' };
aurelia.use
.standardConfiguration()
.feature('aurelia-scrollbar', scrollbarConfig);
In your feature's configure method, register the config object in the container.
aurelia-scrollbar/index.js
export function configure(frameworkConfiguration, scrollbarConfig) {
frameworkConfiguration.globalResources('./scrollbar');
frameworkConfiguration.container.registerInstance('scrollbar-config', scrollbarConfig);
}
Anything that depends on the configuration can use the container to retrieve it:
aurelia-scrollbar/scrollbar.js
#inject(Element, 'scrollbar-config')
export class Scrollbar {
constructor(element, scrollbarConfig) {
...
}
...
}
GitHub issue to add this information to the Aurelia documentation: https://github.com/aurelia/framework/issues/570

Related

How to define method on 3rd party class using typescript?

I'm trying to extend a 3rd party class but am having trouble getting typescript to play nice. Basically, I can't use any existing method already defined in the class in my new method.
A workaround would be to redefine existing methods in extensions.ts (see below), but there just has to be a better way.
3rd party index.d.ts
export as namespace thirdParty;
export Class SomeClass {
// some methods here
}
My extensions.ts
import {thirdParty} from 'thirdParty'
declare module 'thirdParty' {
namespace thirdParty {
class SomeClass{
newMethod(): this
// works if I redfine the method here
originalExistingMethod(): number
}
}
}
thirdParty.SomeClass.prototype.newMethod = function() {
return this.originalExistingMethod() + 1
}
When calling an existing method like this.originalExistingMethod() above, typescript complains:
TS2339: Property 'originalExistingMethod' does not exist on type 'SomeClass'
Is there a way to avoid having to redefine existing methods when performing module augmentation?
Initial Answer
Here is an example using the Tensorflow library.
extend.ts
import { AdadeltaOptimizer } from '#tensorflow/tfjs-core';
declare module '#tensorflow/tfjs-core' {
interface AdadeltaOptimizer {
newMethod(message: string): void;
}
}
AdadeltaOptimizer.prototype.newMethod = function (message: string) {
console.log('===============');
console.log(message);
console.log('===============');
}
index.ts
import { AdadeltaOptimizer } from '#tensorflow/tfjs';
import "./extend";
const optimizer = new AdadeltaOptimizer(10, 10);
// the existing method is present
const className = optimizer.getClassName();
// the augmentation is also present
optimizer.newMethod(`The className is ${className}.`);
There is a similar example in the official TypeScript documentation, which augments Observable with a map method.
Follow Up on Comments
Thanks. Though my issue is using existing methods when defining newMethod. So in extend.ts not in index.ts. Any ideas on this?
This also works in extend.ts as follows:
import { AdadeltaOptimizer } from '#tensorflow/tfjs-core';
declare module '#tensorflow/tfjs-core' {
interface AdadeltaOptimizer {
newMethod(message: string): void;
}
}
AdadeltaOptimizer.prototype.newMethod = function (message: string) {
// just access the original method on `this`
const className = this.getClassName();
console.log('===============');
console.log(className);
console.log(message);
console.log('===============');
}

How to use Vue plugin in Store?

Is there a proper / documented way of using a plugin inside vuex module or plain js module?
I am using event bus to acheive it, not sure if it is the correct / best way. Please help.
Plugin1.plugin.js:
const Plugin1 = {
install(Vue, options) {
Vue.mixin({
methods: {
plugin1method(key, placeholderValues = []) {
return key;
},
},
});
},
};
export default Plugin1;
In App.vue:
Vue.use(Plugin1, { messages: this.plugin1data });
In store / plain-js module:
const vue = new Vue();
const plugin1method = vue.plugin1method;
you can access your Vue instance using this._vm;
and the Vue global using import Vue from 'vue'; and then Vue;
I'm guessing you defined an instance method, so it would be the former (this._vm.plugin1method())
update
I can't tell you which way you should use it because it I can't see how your function is defined in your plugin.
However, here is an example that should illustrate the difference between instance and global
const myPlugin = {
install: function(Vue, options) {
// 1. add global method or property
Vue.myGlobalMethod = function() {
// something logic ...
console.log("run myGlobalMethod");
};
Vue.mixin({
methods: {
plugin1method(key, placeholderValues = []) {
console.log("run mixin method");
return key;
}
}
});
// 4. add an instance method
Vue.prototype.$myMethod = function(methodOptions) {
console.log("run MyMethod");
// something logic ...
};
}
};
Vue.use(Vuex);
Vue.use(myPlugin);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
this._vm.$myMethod();
Vue.myGlobalMethod();
this._vm.$options.methods.plugin1method(); // <-- plugin mixin custom method
state.count++;
}
}
});
when you commit the increment ie: this.$store.commit('increment') both methods will execute

Nightwatch: Override Function in Page Object

I have created a page object model in Nightwatch but I would like to override some functions for customization. How would I achieve that?
module.exports = {
url: 'http://localhost:63916/Login/Login',
commands: [{
navigateToImportBatch() {
this
.click('#importManager')
.click('#importBatch')
return this.api;
}
};
How can I override the navigateToImportBatch function? Thanks.
Override globally:
// your current module to override, e.g. loginPageModel.js
let module = {
url: 'http://localhost:63916/Login/Login',
commands: [{
navigateToImportBatch() {
this.click('#importManager')
.click('#importBatch');
return this.api;
}
}]
};
module.exports = module;
And then define a new module, override whatever commands you need to and export it.
// loginOverride.js
let LoginOverride = require('./loginPageModel'); // whatever the path to your module is called
LoginOverRide.commands[0].navigateToImportBatch = function() {
// your new implementation
}
module.exports = LoginOverride;
Then simply import the module that overrides the base implementation in your step definition/s.
let LoginModel = require('./loginOverride');
// use as normal
Alternately you can use the same strategy in a step definition without defining a new page model, and just override the implementation from the step definition.

How to dynamically extend class with method in Typescript from remote js file?

I have TypeScript code with classes of components. And I want to use somehow remote js file to extend this classes remote. So I want when my app starts to get js file remote and use code this for extend of needed class.
How to extend class I know. For example:
Import { UsersBlocksMyOrders } from "../pages/users/blocks/myorders";
declare module "../pages/users/blocks/myorders" {
interface UsersBlocksMyOrders {
logit(): void;
}
}
UsersBlocksMyOrders.prototype.logit = function () { console.log(this); }
In component file the code is:
import { APP_CONFIG } from "../../../app/app.config";
#Component({
selector: 'menu-blocks-menupage',
templateUrl: APP_CONFIG.appDomain + '/mobilesiteapp/template/?path=pages/menu/blocks/menupage'
})
export class MenuBlocksMenuPage{
constructor(){
this.logit();
}
}
My problem is that I use the Webpack to compile code. Webpack create final file where name of function is different. That's why I can't access to class directly.
How to be in this situation?
Create service to get file and extend class. You need to have variable inside class which is would store object where have keys. Keys it is names of classes to extend. And values with imported classes. Inside init function we are loop extending with prototype method.
import { Http } from "#angular/http";
import { MenuBlocksMenuPage } from "../pages/menu/blocks/menupage";
export class ExtendService {
allModules = {
...
MenuBlocksMenuPage : MenuBlocksMenuPage,
...
};
constructor(public http: Http){ }
init()
{
//Load json map to extend class
this.http.get(APP_CONFIG.appDomain + '/modules/mobilesiteapp/view/js/extend.json').toPromise()
.then((res) => {
let json = res.json();
//Loop each class to extend
Object.keys(json).forEach((cl) => {
//Add new functions and methods
Object.keys(json[cl]).forEach((func) => {
this.allModules[cl].prototype[func] = eval(json[cl][func]);
});
});
}).catch((e) => {
console.error(e);
});
}
}
Request json file with functions to eval.
{
"MenuBlocksMenuPage": {
"logit": "(function (){console.log(this);})"
}
}

How can I mock the imports of an ES6 module?

I have the following ES6 modules:
File network.js
export function getDataFromServer() {
return ...
}
File widget.js
import { getDataFromServer } from 'network.js';
export class Widget() {
constructor() {
getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
render() {
...
}
}
I'm looking for a way to test Widget with a mock instance of getDataFromServer. If I used separate <script>s instead of ES6 modules, like in Karma, I could write my test like:
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(window, "getDataFromServer").andReturn("mockData")
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
However, if I'm testing ES6 modules individually outside of a browser (like with Mocha + Babel), I would write something like:
import { Widget } from 'widget.js';
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(?????) // How to mock?
.andReturn("mockData")
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
Okay, but now getDataFromServer is not available in window (well, there's no window at all), and I don't know a way to inject stuff directly into widget.js's own scope.
So where do I go from here?
Is there a way to access the scope of widget.js, or at least replace its imports with my own code?
If not, how can I make Widget testable?
Stuff I considered:
a. Manual dependency injection.
Remove all imports from widget.js and expect the caller to provide the deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
I'm very uncomfortable with messing up Widget's public interface like this and exposing implementation details. No go.
b. Expose the imports to allow mocking them.
Something like:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
then:
import { Widget, deps } from 'widget.js';
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(deps.getDataFromServer) // !
.andReturn("mockData");
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
This is less invasive, but it requires me to write a lot of boilerplate for each module, and there's still a risk of me using getDataFromServer instead of deps.getDataFromServer all the time. I'm uneasy about it, but that's my best idea so far.
I've started employing the import * as obj style within my tests, which imports all exports from a module as properties of an object which can then be mocked. I find this to be a lot cleaner than using something like rewire or proxyquire or any similar technique. I've done this most often when needing to mock Redux actions, for example. Here's what I might use for your example above:
import * as network from 'network.js';
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(network, "getDataFromServer").andReturn("mockData")
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
If your function happens to be a default export, then import * as network from './network' would produce {default: getDataFromServer} and you can mock network.default.
Note: the ES spec defines modules as read-only, and many ES transpilers have started honoring this, which may break this style of spying. This is highly dependent on your transpiler as well as your test framework. For example, I think Jest performs some magic to make this work, though Jasmine does not, at least currently. YMMV.
carpeliam is correct, but note that if you want to spy on a function in a module and use another function in that module calling that function, you need to call that function as part of the exports namespace, otherwise the spy won't be used.
Wrong example:
// File mymodule.js
export function myfunc2() {return 2;}
export function myfunc1() {return myfunc2();}
// File tests.js
import * as mymodule
describe('tests', () => {
beforeEach(() => {
spyOn(mymodule, 'myfunc2').and.returnValue = 3;
});
it('calls myfunc2', () => {
let out = mymodule.myfunc1();
// 'out' will still be 2
});
});
Right example:
export function myfunc2() {return 2;}
export function myfunc1() {return exports.myfunc2();}
// File tests.js
import * as mymodule
describe('tests', () => {
beforeEach(() => {
spyOn(mymodule, 'myfunc2').and.returnValue = 3;
});
it('calls myfunc2', () => {
let out = mymodule.myfunc1();
// 'out' will be 3, which is what you expect
});
});
vdloo's answer got me headed in the right direction, but using both CommonJS "exports" and ES6 module "export" keywords together in the same file did not work for me (Webpack v2 or later complains).
Instead, I'm using a default (named variable) export wrapping all of the individual named module exports and then importing the default export in my tests file. I'm using the following export setup with Mocha/Sinon and stubbing works fine without needing rewire, etc.:
// MyModule.js
let MyModule;
export function myfunc2() { return 2; }
export function myfunc1() { return MyModule.myfunc2(); }
export default MyModule = {
myfunc1,
myfunc2
}
// tests.js
import MyModule from './MyModule'
describe('MyModule', () => {
const sandbox = sinon.sandbox.create();
beforeEach(() => {
sandbox.stub(MyModule, 'myfunc2').returns(4);
});
afterEach(() => {
sandbox.restore();
});
it('myfunc1 is a proxy for myfunc2', () => {
expect(MyModule.myfunc1()).to.eql(4);
});
});
I implemented a library that attempts to solve the issue of run time mocking of TypeScript class imports without needing the original class to know about any explicit dependency injection.
The library uses the import * as syntax and then replaces the original exported object with a stub class. It retains type safety so your tests will break at compile time if a method name has been updated without updating the corresponding test.
This library can be found here: ts-mock-imports.
I have found this syntax to be working:
My module:
// File mymod.js
import shortid from 'shortid';
const myfunc = () => shortid();
export default myfunc;
My module's test code:
// File mymod.test.js
import myfunc from './mymod';
import shortid from 'shortid';
jest.mock('shortid');
describe('mocks shortid', () => {
it('works', () => {
shortid.mockImplementation(() => 1);
expect(myfunc()).toEqual(1);
});
});
See the documentation.
I haven't tried it myself, but I think mockery might work. It allows you to substitute the real module with a mock that you have provided. Below is an example to give you an idea of how it works:
mockery.enable();
var networkMock = {
getDataFromServer: function () { /* your mock code */ }
};
mockery.registerMock('network.js', networkMock);
import { Widget } from 'widget.js';
// This widget will have imported the `networkMock` instead of the real 'network.js'
mockery.deregisterMock('network.js');
mockery.disable();
It seems like mockery isn't maintained anymore and I think it only works with Node.js, but nonetheless, it's a neat solution for mocking modules that are otherwise hard to mock.
I recently discovered babel-plugin-mockable-imports which handles this problem neatly, IMHO. If you are already using Babel, it's worth looking into.
See suppose I'd like to mock results returned from isDevMode() function in order to check to see how code would behave under certain circumstances.
The following example is tested against the following setup
"#angular/core": "~9.1.3",
"karma": "~5.1.0",
"karma-jasmine": "~3.3.1",
Here is an example of a simple test case scenario
import * as coreLobrary from '#angular/core';
import { urlBuilder } from '#app/util';
const isDevMode = jasmine.createSpy().and.returnValue(true);
Object.defineProperty(coreLibrary, 'isDevMode', {
value: isDevMode
});
describe('url builder', () => {
it('should build url for prod', () => {
isDevMode.and.returnValue(false);
expect(urlBuilder.build('/api/users').toBe('https://api.acme.enterprise.com/users');
});
it('should build url for dev', () => {
isDevMode.and.returnValue(true);
expect(urlBuilder.build('/api/users').toBe('localhost:3000/api/users');
});
});
Exemplified contents of src/app/util/url-builder.ts
import { isDevMode } from '#angular/core';
import { environment } from '#root/environments';
export function urlBuilder(urlPath: string): string {
const base = isDevMode() ? environment.API_PROD_URI ? environment.API_LOCAL_URI;
return new URL(urlPath, base).toJSON();
}
You can use putout-based library mock-import for this purpose.
Let's suppose you have a code you want to test, let it be cat.js:
import {readFile} from 'fs/promises';
export default function cat() {
const readme = await readFile('./README.md', 'utf8');
return readme;
};
And tap-based test with a name test.js will look this way:
import {test, stub} from 'supertape';
import {createImport} from 'mock-import';
const {mockImport, reImport, stopAll} = createMockImport(import.meta.url);
// check that stub called
test('cat: should call readFile', async (t) => {
const readFile = stub();
mockImport('fs/promises', {
readFile,
});
const cat = await reImport('./cat.js');
await cat();
stopAll();
t.calledWith(readFile, ['./README.md', 'utf8']);
t.end();
});
// mock result of a stub
test('cat: should return readFile result', async (t) => {
const readFile = stub().returns('hello');
mockImport('fs/promises', {
readFile,
});
const cat = await reImport('./cat.js');
const result = await cat();
stopAll();
t.equal(result, 'hello');
t.end();
});
To run test we should add --loader parameter:
node --loader mock-import test.js
Or use NODE_OPTIONS:
NODE_OPTIONS="--loader mock-import" node test.js
On the bottom level mock-import uses transformSource hook, which replaces on the fly all imports with constants declaration to such form:
const {readFile} = global.__mockImportCache.get('fs/promises');
So mockImport adds new entry into Map and stopAll clears all mocks, so tests not overlap.
All this stuff needed because ESM has it's own separate cache and userland code has no direct access to it.
Here is an example to mock an imported function
File network.js
export function exportedFunc(data) {
//..
}
File widget.js
import { exportedFunc } from 'network.js';
export class Widget() {
constructor() {
exportedFunc("data")
}
}
Test file
import { Widget } from 'widget.js';
import { exportedFunc } from 'network'
jest.mock('network', () => ({
exportedFunc: jest.fn(),
}))
describe("widget", function() {
it("should do stuff", function() {
let widget = new Widget();
expect(exportedFunc).toHaveBeenCalled();
});
});
I haven't been able to try it out yet, but (Live demo at codesandbox.io/s/adoring-orla-wqs3zl?file=/index.js)
If you have a browser-based test runner in theory you should be able to include a Service Worker that can intercept the request for the ES6 module you want to mock out and replace it with an alternative implementation (similar or the same as how Mock Service Worker approaches things)
So something like this in your service worker
self.addEventListener('fetch', (event) => {
if (event.request.url.includes("canvas-confetti")) {
event.respondWith(
new Response('const confetti=function() {}; export default confetti;', {
headers: { 'Content-Type': 'text/javascript' }
})
);
}
});
If your source code is pulling in an ES6 module like this
import confetti from 'https://cdn.skypack.dev/canvas-confetti';
confetti();

Categories