Using babel-plugin-rewire to test private non-referenced functions - javascript

I am using babel-plugin-rewire (https://www.npmjs.com/package/babel-plugin-rewire) in main.test.js to test non-exported functions in main.js. This has been working except in the case where the function is not referenced in main.js; in this case I get the following error: TypeError: _get__(...) is not a function.
Only after I add a reference to the function in main.js I am able to access it in the test file (it works even if I don't actually call the function). However I do not want to make any changes to main.js. Is this the expected behavior of babel-plugin-rewire, and is there a workaround for this?
//main.js
function test123() {
return true;
}
test123; //Cannot access function in test file unless I add this reference!
//main.test.js
const test123 = require('./main').__get__('test123');
test('test123', () => {
expect(test123()).toEqual(true);
});

You can test non-exported functions in main.js, but the non-exported functions need to be used in at least one exported function in the same file.
In your case, this will work without a reference.
//main.js
export default function foo() {
test123();
}
function test123() {
return true;
}

Related

webpack doesnt add prototype function to Object from other file

I have a file containing the definition of a Object and in that same file I have a function that is part of this object like so:
export function ARScene(_callbacks) {
this.callbacksObject = _callbacks;
// more fancy code..
}
ARScene.prototype.changeCar = function() {
//some fancy code here
this.loadHDCar(); // THIS LIKE GENERATES A ERROR.
}
now I have a different file containing an other method that is part of the Object called ARScene like so:
import { ARScene } from './arScene';
ARScene.prototype.loadHDCar = function() {
//some more fancy code..
}
What is happening when I build this with webpack and run it in the browser I get the error that this.loadHDCar(); is undefined I guess this happens because webpack doesnt add a file if it is not imported. But how do I make sure that ARScene.prototype.loadHDCar is added to the object in the final output?
I am a complete newbie to webpack and modules. I have found answers on stackoverflow about this but they had slightly different scenarios then me. So their solutions didnt work (or maybe I didnt understand it).
If more context or information is needed please let me know.
How do I make sure that ARScene.prototype.loadHDCar is added to the object in the final output?
You should import it in the arScene module, and you should even create the prototype method in there (where you are defining the class) for visibility.
export function loadHDCar() {
… //some more fancy code
}
import { loadHDCar } from './loadHDCar';
export function ARScene(_callbacks) {
…
}
ARScene.prototype.loadHDCar = loadHDCar;
ARScene.prototype.changeCar = function() {
… // some fancy code here
this.loadHDCar();
};

What will happen after bundling 2 .js files which have a function with same name and Parameter using bundle.config?

If two .js files having functions with the same name and parameter but the code is different. What will happen after bundling using bundle.config?
Which one will call?
Or/And how can we fix that?
//in parent.js file
function getdetails(id)
{
//enter code here
}
//in student.js file
function getdetails(id)
{
//enter code here
}
Depending on the type of bundler, normally functions are scoped from the modules they are exported from.
So for example:
//in parent.js file
function getdetails(id)
{
//enter code here
}
//in student.js file
function getdetails(id)
{
//enter code here
}
In the bundled javascript, they become (as an general example):
_parent.getdetails()
_student.getdetails()
You could check your un-minified bundled code and search for getdetails to see how the functions are being scoped for clarification.
Side note
If two functions are being defined with the same name, in the same scope (which shouldn't be the case here), then the function re-defined each time.
//in parent.js file
function getdetails() {console.log("foo");}
function getdetails() {console.log("bar");}
getdetails(); // Outputs "bar"

Does Node run all the code inside required modules?

Are node modules run when they are required?
For example: You have a file foo.js that contains some code and some exports.
When I import the file by running the following code
var foo = require(./foo.js);
is all the code inside the file foo.js run and only exported after that?
Much like in a browser's <script>, as soon as you require a module the code is parsed and executed.
However, depending on how the module's code is structured, there may be no function calls.
For example:
// my-module-1.js
// This one only defines a function.
// Nothing happens until you call it.
function doSomething () {
// body
}
module.exports = doSomething;
// my-module-2.js
// This one will actually call the anonymous
// function as soon as you `require` it.
(function () {
// body
})();
Some examples..
'use strict';
var a = 2 * 4; //this is executed when require called
console.log('required'); //so is this..
function doSomething() {}; //this is just parsed
module.exports = doSomething; //this is placed on the exports, but still not executed..
Only in the sense that any other JS code is run when loaded.
e.g. a function definition in the main body of the module will be run and create a function, but that function won't be called until some other code actually calls it.
Before exporting the content that are visible outside of your module, if there is same code that can be execute it it execute but the content that are export like a class will be execute in the code that import it.
For example, if I have this code
console.log("foo.js")
module.exports = {
Person: function(){}
}
the console.log will be execute when you require it.

How do I test a function which returns a function using Mocha, Sinon, and Chai?

I have a function which returns another function of the same name. Inside the second function, functions from other modules are being used. I just want to test whether the functions from other modules are being called.
Here is some code to clarify what I mean:
exports.getCache = function (model) {
return function getCache (req, res){
//some code
key = utils.uniqueKey(model, id)
//some code
res.json(result);
}
}
I want to check if uniqueKey is being called and if res.json is being called.
Any help at all is appreciated, thanks!
You can simply write:
sinon.spy(utils, 'uniqueKey')
in your test file, probably inside beforeEach function. Then it should be easy to check with chai.expect function whenever it was called:
expect(utils.uniqueKey.called).to.be.true();
expect(utils.uniqueKey.calledWith({modelValue: 'value'}, 'someId')).to.be.true();
where {modelValue: 'value'} and 'someId' are the actual values of model and id variables.
To make your component testable you have to make its dependencies injectable. As I see your code depends on utils. To properly mock this you can't just require('utils'), you have to implement some way to wire up dependencies. Using some DI library, or just extend public interface of your component:
var utils;
module.exports.dependencies = function (_utils) {
utils = _utils;
};
module.exports.getCache = function (model) {
key = utils.uniqueKey(model, id);
};
Then in productive code before using bootstrap your component calling component.dependencies(require('utils')). In test case you can pass there a spy: component.dependencies(spy);

Global variables in Karma test runner

I have a global variable defined in my main template, which I use to store information bits from the back end, such as the environment context path. I can't move that variable inside a service.
How can I expose that variable to Karma when I run the unit tests?
You either declare that global variable within your test file:
var global = "something";
describe('Your test suit', function() {
...
});
or add a Javascript file where it's defined to your karma.conf.js file:
// list of files / patterns to load in the browser
files: [
...,
'file-containing-the-global-variable.js'
],
If you are coming from Angular 2+ the only way I found that works is to create the variable or object globally using window:
Google Recapthca Loaded from a script:
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
In this example a call to the google Recaptcha will create a global variable called grecaptcha.
In our component we decide to render a new Google Recaptcha. Of course since we do not declare grecaptcha we need to use declare var to say hey I promise it is declared somewhere:
declare var grecaptcha;
private CreateGoogleCaptcha() {
grecaptcha.render('recaptcha', {
sitekey: this.siteKey,
callback: this.GoogleCaptchaCallback,
badge: 'inline'
});
}
private GoogleCaptchaCallback(token) {
// Handle Callback Logic
}
Now that our function works we decide to run a test but of course we would like to mock our grecaptcha as we don't have control over it.
So we name our global variable we would like to create and add the functions we would like:
beforeEach(() => {
fixture = TestBed.createComponent(GoogleRecaptchaComponent);
component = fixture.componentInstance;
window['grecaptcha'] = {
render() {
console.log('mocked global variable and function');
}
}
}
Update:
Creating the global variable in the beforeEach is fine but what about when it has some sort of callback function such as above that calls a function from your component? Easy enough we just move the login to our test and in our mock we set it to our components function:
it('should ', () => {
window['grecaptcha'] = {
render: function() { GoogleRecaptchaComponent['GoogleCaptchaCallback']('token'); }
};
spyOn<any>(GoogleRecaptchaComponent, 'GoogleCaptchaCallback');
GoogleRecaptchaComponent['CreateGoogleCaptcha']();
expect(GoogleRecaptchaComponent['GoogleCaptchaCallback']).toHaveBeenCalled();
});
Note: spyOn<any>: The <any> is used so we can refence without error the function because it is private, otherwise we will get a typescript error;
The first solution didn't work for me in Angular 2.1.x. It simply would not recognize the variable in my imported service. What I had to do was put my environment variable in my karma-test-shim.js file and remove var so it would be globally available.
Mine looks like this:
Error.stackTraceLimit = Infinity;
require('core-js/es6');
require('reflect-metadata');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy'),
require('zone.js/dist/sync-test'),
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
// Add environment variables here so that tests will inject them in source code
API_URL = 'http://localhost:8080/api/';
var appContext = require.context('../src', true, /\.spec\.ts/);
appContext.keys().forEach(appContext);
var testing = require('#angular/core/testing');
var browser = require('#angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(
browser.BrowserDynamicTestingModule,
browser.platformBrowserDynamicTesting()
);
I was trying to integrate angular v13 with google-place-autocomplete without any npm package, just vanilla google-place-autocomplete. When i tried to test, karma said: '"google" is not defined'.
So i found the awswers above, and it worked for me like this:
Created a file in root directory (in my case, i named: google-place.js)
Added a mock object with the instructions that is used in component.
class Autocomplete {
constructor(input, options) {
const addListener = (eventName, callback) => { }
const getPlace = () => { }
return { addListener, getPlace }
}
}
var google = {
maps: {
places: {
Autocomplete: Autocomplete
}
}
};
Added in karma.conf.js a property called "files", and end up like this:
files: [
"./google-place.js"
]
And it was not necessary to add a declare var google in every spec. Is global now.

Categories

Resources