Testing React component with global dependency - requirejs

I have included a JS dependency (Foo.js) in my index.html file. When I call Foo.js in my React component, it finds the Constructor in the global namespace and instantiates it. This works great when I deploy it, but when I go to build a test around Component.js, the test can't find Foo.js
<!--Index.html-->
<head>
<script src="Foo.js"></script>
</head>
// Component.js
var bar = new Foo(); // Works in deployment but not in Jest tests
When running my test I get this error:
RefererenceError: Foo is not defined
Now I thought I would be clever and in my Component.js file declare Foo to be window.Foo, which worked in getting rid of the undefined dependency in my Jest tests.
// Component.js
var Foo = window.Foo;
var bar = new Foo();
Suddenly my Reference error went away and I was happy. So I continue to write tests and now I get a funky error that I think has to do with that global dependency again.
TypeError: undefined is not a function
I believe that my error is still coming from Jest not properly mocking a dependency that is on the window object. I don't need to test the dependency, I just need it to be defined so I can get to writing tests for the rest of the Component. Does anyone have a thought on what I could possibly be doing wrong?

So I finally figured out how to fix this. In my browser environment and my testing environment I have two completely separate window objects. In my test, before I require in my component I have to set window.Foo to an anonymous function. It will look like this:
// Component.js
var bar = new Foo(); // Works in browser but not in test
// ...Rest of code
// Component.test.js
describe('Component.js', function() {
let Component;
beforeEach(function() {
window.Foo = function() {};
Component = require('./Component.js'); // When it requires in component,
// it will now have Foo declared on the window object
});
});
I have to explicitly declare any window objects in my test environment for any components to find those functions.

Related

TypeScript global variable is not defined, declared in declare global{}

I'm trying to add a global scope variable (which means it can be accessed from anywhere without having to import/require it from another file) in TypeScript.
If this is in JavaScript (NodeJS), it would look like this:
// index.js
globalThis.helloWorld = 'Hello World!';
require('./log.js')
// log.js
console.log(helloWorld);
// output
Hello World!
What I'm struggling against in TypeScript:
// types.ts
// ...
declare global{
var GLOBAL_EVENTS: EventEmitter
}
// ...
// app.ts
// ...
globalThis.GLOBAL_EVENTS = new EventEmitter()
// ...
// usage in another file
// ...
GLOBAL_EVENTS.on(...)
// ...
// output:
ReferenceError: GLOBAL_EVENTS is not defined
I've tried using global.GLOBAL_EVENTS too, but to no avail, it works. Where did I go wrong?
The fact it's a runtime error (ReferenceError) tells you this isn't a TypeScript problem. The problem is that the GLOBAL_EVENTS.on(...) code is running before the globalThis.GLOBAL_EVENTS = new EventEmitter(); code in app.ts, so GLOBAL_EVENTS (although defined in TypeScript) doesn't exist yet at runtime.
You need to make sure app.ts runs first, before the other file, so GLOBAL_EVENTS gets created before being used.
(This is one of the good reasons for not using globals and using an export from a module instead: Then the module system would ensure that module is loaded and ready before its exports are used.)

Using aws-sdk.js with require.js in a browser

I'm trying to figure out how to use the browser-based aws-sdk.js with require.js when building a web app.
If I try to build a class and include aws-sdk in the require.js define, once I try to create an instance and reference the AWS class, it says it is undefined.
For example, I have a function that checks to see if the credentials are an instance of AWS.Credentials and if not, it tries to initialize it with an STS token it retrieves via Rest.
The problem is, the code dies on the if(this.credentials instanceof AWS.Credentials) saying AWS is undefined. It even dies on my simple check of the needsRefresh.
This is what my define looks like - I'll include the 'needsRefresh' wrapper for an example of the sort of thing that is throwing the Undefined error:
define(['require','aws-sdk','jquery'],
function (require, AWS, $) {
// Aws helper class
function AwsHelper() { };
AwsHelper.prototype = {
credentials: null,
tokenNeedsRefresh: function () {
//////////////////////////////////////////////////////////////////
// errors out on the following line with: //
// TypeError: Cannot read property 'Credentials' of undefined //
//////////////////////////////////////////////////////////////////
if(this.credentials instanceof AWS.Credentials) {
return this.credentials.needsRefresh();
} else return true;
}
};
return AwsHelper;
}
);
I also tried the following format at the top of the file:
define(function (require) {
var AWS = require("aws-sdk"),
$ = require("jquery");
/* .. */
If I remove all onLoad references to the refresh code running, it will load and I can create an instance. But as soon as I call any function that references the AWS class, it dies.
How do I make sure that AWS class definition is still in global space once an instance is spawned?
Dunno what difference the paths will make (it's finding and loading the code just fine - I can see AWS Class in namespace in the debugger as it's loading but it's not in the namespace on the function call), but added on request:
requirejs.config({
baseUrl: '/js',
paths: {
lib: 'lib',
ImageUploader: 'ImageUploader'
}
});
I decided to try to play with this again and seemed to have figured out how I was going about it incorrectly. I have yet to integrate it back into my existing code setup, but I think I have a working solution.
The thing I was doing wrong was I was trying to set up the AWS class so I could load it as a module (thus why I tried wrapping it as the require.js suggested).
Playing with it this time around, I noticed something I hadn't before. I had an AWS that was undefined in the local scope and an other AWS that held the class definition in the global scope. So it was trying to specify AWS in my define that was creating the local 'null' version:
define(
["jquery","aws-sdk","dropzone","app/MyUtilities"],
function ($, AWS, Dropzone, MyUtilities) {
"use strict";
function MyClass() {};
return MyClass;
}
);
jquery and dropzone have whatever is needed to make sure they load that way, but aws-sdk seems to do some of it's own asynchronous loading. Thus the scope variable in the function is undefined.
Technically, it seems the only thing I needed defined as a module in the function wrapper is my own utilities module. So by switching it to:
define(
["app/MyUtilities","jquery","aws-sdk","dropzone"],
function (MyUtilities) {
"use strict";
function MyClass() {};
return MyClass;
}
);
... it seems $ and jquery are defined as needed, Dropzone is defined as needed and AWS is defined as needed. (and I don't get my errors when accessing the static AWS.config or AWS.Credentials definitions as I did before)

How to access a closure function in a require.js module?

I am trying to use require.js to load my modules dependencies and so far it is working, but I have a doubt. I've created a little function to test the modules and placed it in a file called panelTest.js:
define(['./panel/View', './panel/TitleView'], function(View, TitleView) {
return function test(container) {
// main view
var panel = new View(container, 'main');
var panelTitle = new TitleView(panel.getContainer(), 'main-title');
panelTitle.setTitle('Properties Panel');
//panelTitle.addCss('pjs-panelTitle');
panel.addView(panelTitle);
// sections
var top = new View(panel.getContainer(), 'top');
panel.addView(top);
var middle = new View(panel.getContainer(), 'middle');
panel.addView(middle);
var bottom = new View(panel.getContainer(), 'bottom');
panel.addView(bottom);
};
});
In the html that uses the modules I included this script tag as shown in the require.js documentation to load panelTest.js.
<script data-main="panelTest.js"
src="require.js"></script>
My question is how can I call the test function from outside the module, since the container parameter it is supposed to come from outside the module.
You have to access the module through the appropriate channels provided by RequireJS. You could do it like this in a script tag that appears after the one that loads RequireJS:
require(['panelTest'], function (panelTest) {
panelTest(/* some value */);
});
Given the code you show, your panelTest module does not seem to really make sense as a "main module" so I would not put it as data-main.
If you want to use it from anther module, put the module in its own file and define it like this:
define(['panelTest'], function (panelTest) {
panelTest(/* some value */);
});

Electron app: Reference mainWindow object in another module?

I am building an electron app, where the mainWindow object is created following the quick start: http://electron.atom.io/docs/tutorial/quick-start/.
As per this quick start, it is created asynchronously. The problem that I run into, is that for instance when I want to send messages from main to renderer process, I need to reference the mainWindow object. If this happens to be in a module that I require, then I need a means to make this module know of the mainWindow object.
I could of course prepend it with global., but I know that this is very much advised against. So I wish to do it more elegantly.
I came across this post: Asynchronous nodejs module exports; which appears to offer a solution. Taking the main.js file from the quick start (see above link, it's explicitly shown there), it appears I would add to the createWindow function
if( typeof callback === 'function' ){
callback(mainWindow);
}
and export the main.js module as
module.exports = function(cb){
if(typeof mainWindow !== 'undefined'){
cb(mainWindow);
} else {
callback = cb;
}
}
Then, in a higher-level script, I would require as follows:
let main = require('./main.js');
let lib = require('./lib.js'); // Library where I need a mainWindow reference
main(function(window) {
lib.doSomething(window);
});
where lib.js looks like
module.exports.doSomething = function(window) {
// Do something with window object, like sending ipc messages to it
window.webContents.send('hello-from-main', "hi!");
}
Although the simple case in the original post 'Asynchronous nodejs module exports' works fine, I cannot get it to work like described above; running the app it complains Uncaught Exception: TypeError: Cannot read property 'webContents' of null. This is also the case if I directly require lib.js within main()'s callback (which I know is also advised against).
I confess that I do not fully understand the simple case of the post, as I am rather new to node. This prevents me from fixing my own implementation of it, which I agree is blunt copy/pasting which reasonably should be expected to fail. Could somebody help me with how to correct above method, or advise me of a different approach to make it work? Thank you!
I have created the npm package electron-main-window for the same.
Install:
$ npm install electron-main-window
or
$ yarn add electron-main-window
Usage:
// Import ES6 way
import { getMainWindow } from 'electron-main-window';
const mainWindow = getMainWindow();
// Import ES5 way
const mainWindow = require('electron-main-window').getMainWindow();
// e.g:
if(mainWindow !== null ){
mainWindow.webContents.send('mainWindowCommunication', "This is a test message");
}
Whooops! The devil is in the details... I had defined on top of main.js
let mainWindow = null, callback;
which caused the error! Should be
let mainWindow, callback;
then it works perfectly!
P.s. Instead of deleting my post, I opted for keeping it and answering myself for future reference of other people who need asynchronous exporting.

RequireJS issues accessing app object across app

I have an application which has an app object which does the start routine and stores useful things like app.state and app.user. However I am trying to access this app instance without passing this from the app instance all the way around my large codebase.
Strangely I work on other projects which include app in the same way as in something.js and it works but I can't see why.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Cannot require app in another file</title>
</head>
<body>
<script data-main="config" src="require.js"></script>
</body>
</html>
config.js
requirejs.config({
deps: ['app']
});
app.js
define([
'something'
], function(Something) {
'use strict';
var App = function() {
this.name = 'My app';
};
return new App();
});
something.js
define([
'require',
'app'
], function (require, app) {
'use strict';
var SomeModule = function() {
app = require('app'); // EXCEPTION
console.log('App:', app);
};
return new SomeModule();
});
When loading this requirejs exception is throw because of the require in SomeModule:
Uncaught Error: Module name "app" has not been loaded yet for context: _
Demo of above (see console for error): http://dominictobias.com/circulardep/
It's not clear to me why you need to have a circular dependency. As stated in the documentation for RequireJS:
Circular dependencies are rare, and usually a sign that you might want to rethink the design.
This being said, if you do need the circular dependency, the issue with your code is that require('app') is called too early. It cannot be called until after the module something has returned its value. Right now, it is called before the value is returned. If you look at the code given as example in the documentation:
define(["require", "a"],
function(require, a) {
//"a" in this case will be null if a also asked for b,
//a circular dependency.
return function(title) {
return require("a").doSomething();
}
}
);
you see that the module returns a function which then would be called by the code that required the module, which happens after this module has returned its value.
So how do you fix this? What you could do is have the class you return call a function that fetches module app whenever needed. So:
define([
'require',
'app'
], function (require) {
'use strict';
var app_;
function fetch_app() {
if (app_ === undefined)
app_ = require("app");
return app_;
}
var SomeModule = function() {
// ...
};
SomeModule.prototype.doSomethingWithApp = function () {
var app = get_app();
app.whatever();
};
return new SomeModule();
});
I've removed app from the list of arguments and store the value of the app module in app_ because doing it this way provides for early detection of a missing call to get_app() in any method of SomeModule. If app is made a parameter of the module's factory function then using app inside a method without calling get_app() first would be detected only if it so happened that no other method that calls get_app() was called first. (Of course, I could type app_ and face the same problem as the one I aim to prevent. It's a matter of respective likelihoods: I'd be very likely to forget to call get_app() everywhere it is needed because I don't usually write code with circular dependencies. However, I'd be unlikely to type app_ for app because I don't usually put _ at the end of my variable names.)

Resources