Global Variables not being accessed by Mocha tests - javascript

I am getting a ReferenceError: NC_SETTINGS is not defined.
"use strict";
import forOwn from 'lodash/object/forOwn';
import { assert, expect, should } from 'chai';
import { spy } from 'sinon';
import { applyMiddleware } from 'redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {
REQUEST_ADD_PASSWORD_RESET_REQUEST,
REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS,
REQUEST_ADD_PASSWORD_RESET_REQUEST_FAILURE
} from 'actions/types';
import nock from 'nock';
import Actions from 'actions';
import fetch from 'isomorphic-fetch';
const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);
var NC_SETTINGS = require('/home/tai/Gravitate/nc-app/taimoor/settings').NC_SETTINGS;
describe('forgot password async actions', () => {
afterEach(()=> {
nock.cleanAll();
});
//mockActions();
//console.log(Actions.addPasswordResetRequest().addPasswordResetRequestAPI());
it('should show a request for a password reset and that it succeeded ', (done) => {
console.log(NC_SETTINGS);
nock('http://localhost:8080/')
.post('/password-reset-requests')
.reply(200);
var email = "test#email.com";
const expectedActions= [
{type: REQUEST_ADD_PASSWORD_RESET_REQUEST},
{type: REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS}
];
const store = mockStore({}, expectedActions, done);
store.dispatch(Actions.addPasswordResetRequest());
//unMockActions();
//
//console.log(actions);
//expect(actions.addPasswordResetRequest("email")).to.equal(expectedAction);
});
});
So when I console.log NC_SETTINGS, it does show. However, when I run store.dispatch(Actions.addPasswordResetRequest), I get the NC_SETTINGS is not defined. The reason why I thought importing NC_SETTINGS could potentially work is because it worked for importing isomorphic-fetch, I had a similar issue.
Is there a way to import global variables into MochaJs such that my action can access NC_SETTINGS?
I should mention that store.dispatch(Actions.addPasswordResetRequest()) links to an actions file, which dispatches to an api call in a js file. That js file at the end is where NC_SETTINGS resides, and where the error is being called. In the browser, this works fine.
Is there a way to have a list of global variables and add them while testing in Mocha?

Don't do this, but you can use:
global.NC_SETTINGS = ...
to set it as a nodejs global variable. And this should be picked up in Actions.
However global variables are something you want to avoid for many, many reasons. Instead you probably want to do some kind of dependency injection. Two possibilities are:
Implement the Actions as a class, and instantiate it where it's used passing NC_SETTINGS as a construction parameter.
Use the rewire module. In that case NC_SETTINGS could be a top level variable in the Actions module, but not a global variable.

Related

Exporting a boolean variable that depends on an if statement (React JS & Firebase)?

I'm trying to declare a boolean from my firebase.js configuration file that depends on an if-else statement that checks whether a user is logged in or not.
I want to store the log in status of a user in a boolean variable and export that variable across different components in my React app.
So if user is logged in I want my variable isLoggedIn to be true and export that. But I want the isLoggedIn boolean to be updated depending on the log-in status before it gets exported.
Here is my firebase.js file
import { initializeApp } from 'firebase/app';
import { getAuth } from "firebase/auth";
const firebaseConfig = {
//private keys
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
//Problem
let isLoggedIn = false;
function checkLoggedInStatus() {
if (auth.currentUser !== null) { //i.e. user is signed in
isLoggedIn = true;
}
return isLoggedIn;
}
checkLoggedInStatus();
export isLoggedIn;
There are (at least) two problems here.
What immediately follows export may be, among other things:
A declaration followed by a variable name (eg export let foo = 5;) - this initializes the variable and exports it from the module in a single line
function or class - similar to the above
default, if you want to indicate what you're exporting is the default export
Brackets, if you're exporting identifiers that have already been declared - eg export { foo }
But doing export isLoggedIn; is invalid syntax - it falls into none of those categories (nor any of the other permitted constructs).
The other issue is that the .currentUser property may not exist yet if the auth isn't done initializing. Make sure you wait for it to be done first. Use the observer version instead.
Since that is asynchronous, you have a few options for getting that information to the rest of your script. The first one, that I'd recommend, is to have this auth module call the rest of your script (in other words, make this module your entry point, or close to it). For example:
import { initializeApp } from 'firebase/app';
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { renderApp } from './renderApp';
const firebaseConfig = {
//private keys
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
onAuthStateChanged(auth, (user) => {
if (user) {
renderApp(user);
} else {
// No user
renderApp();
}
});
where renderApp is the starting point for the rest of your code that depends on the user. You can't easily export isLoggedIn directly from the firebase module because it's retrieved asynchronously - calling renderApp with it and then passing the value around is the best way to do it.
You can also export a Promise from this firebase module script, one that resolves to whether the user is logged in or not, but that'll require all consumers of the module to have to call .then on the value before being able to use it, which could be quite messy. Better to use your firebase module script as an entry point and only call the rest of your script once the auth object has finished loading.

Vuex store modules not loading in right order when importing store directly

I'm probably not seeing obvious, but after hours I don't get it still.
The problem: when I import my Vuex store into a non-component file (a api service), one module gets loaded properly while the other one only by name, but is otherwise empty.
// store.js
import * as collections from "./modules/collections";
import * as auth from "./modules/auth";
export default new Vuex.Store({
modules: {
auth,
collections
}
});
Both these modules are near-identical. Both have
export const getters = {}
export const actions = {}
export const mutations = {}
export const state = {}
Now when in some other non-component file I DON'T include the store, my vue store looks like this:
{"auth":{"authenticated":false,"authToken":"123","loginPending":false,"loginError":{}},"collections":{"collectionsPending":false,"collectionsError":null,"collections":[]}}
Now when I import the store to use it in my service like so:
import store from '../store'
// NO OTHER MENTIONS OF STORE, ONLY IMPORTING
Suddenly only my auth module is "empty"
{"auth":{},"collections":{"collectionsPending":false,"collectionsError":null,"collections":[]}}
It has something to do with module loading.
Order of loading by adding console.log statements:
Without the import:
INIT AUTH
INIT collections
INIT store
With the import:
INIT collections
INIT store
IMPORT STATEMENT -- INTO SERVICE
INIT AUTH
I'm using the Vue Webpack Boilerplate
Sigh, sorry. Circular dependency indeed. Was expecting a warning if I'd did that, but didn't.
Thanks Emile Bergeron.

Global import for testing react app

If I wanted to do this in all my test.js files:
import { shallow } from 'enzyme';
import MockAdapter from 'axios-mock-adapter';
Is there a way to globally import it so that every 'tests.js' file will automatically have that imported?
Thanks in advance!!!
You can use globals!
Example:
setup.js
import { _shallow } from 'enzyme'
import _MockAdapter from 'axios-mock-adapter'
global.shallow = _shallow
global.MockAdapter = _MockAdapter
test1.js
describe('My Test 1', _ => {
MockAdapter() // Use it!
})
Notes:
Global variables will solve this particular issue. But the caveat is that you will have one instance across each test (which might be ok depending on the dependencies you import)
EDIT: In context of react-boiler-plate
If you read package.json, you'll see that there is a test setup file within "jest".
This path is react-boilerplate/internals/testing/test-bundler.js.
Edit that file with the contents of setup.js (aforementioned)
Now each one of your tests should have the variables defined

.then() on node modules not working?

I'm working on a react native app right now and I wrote my own module to store some information separated from my components.
While I did that I noticed that in my module I created, I had a function like this:
testFetch = function(url){
return fetch(url);
}
exports.test = testFetch;
In my main module I did the following:
import ts from 'test-module'
ts.test("http://example.com").then((response) => {console.log(response)});
But .then() is not getting fired for any reason. The request went through and was successful.
Any help on this?
The problem is the way you mix CommonJS and ES6 modules. You seem to expect that ts in your example main module gives you the value of the whole export in your module dependency, but that's not how it works.
You can use export default to export your testFetch function, like so:
testFetch = function (url) {
return fetch(url);
}
export default testFetch;
Main module:
import testFetch from 'test-module';
testFetch("http://example.com").then((response) => {console.log(response)});

ES6: share data between modules

To share data between modules, a usual pattern is to capsulate the data into a common module and import it in other modules.
In my case the data to be shared is a logger, which need to be initialized before used. I call init() at the entry point of my application.
// main.js
let logger = require('#my/logger');
logger.init({...});
let xxx = require('./moduleX');
let yyy = require('./moduleY');
In other modules, the initialized logger can be used:
// moduleX.js
let logger = require('#my/logger');
const log = logger('moduleX');
function x() {
log.debug('some msg');
}
Above code works well in node.js. But if I change to ES6 module syntax, it doesn't work because ES6 module import is hoisted.
// main.js
import {logger} from '#my/logger';
logger.init({...}); // this line is run after import moduleX
import {x} from './moduleX';
// moduleX.js
import {logger} from '#my/logger';
const log = logger('moduleX'); // logger is not initialized !
export function x() {
log.debug('some msg');
}
With ES6 module, how can I initialize some data and share them to other modules?
There was a similar question but the answer doesn't fit my case.
Update:
Some answers suggest to put the code which access shared data into function so that the code isn't invoked immediately at module load. But what if I really need to access it during module loading? I updated my code to demonstrate the use case -- it would be too trivial to call logger(name) in every function if not make log as module scope const.
Finally I solve it in the way that #PaoloMoretti mentioned in his comment.
Write a module in my app to init the logger for my app:
// logger_init.js
import {logger} from '#my/logger';
logger.init({...});
Import the initialization module once at the entry point of application, prior to imports of any other modules that use logger as well. It guarantees that the initialization is done before loading other modules.
// main.js
import './logger_init';
import {x} from '#my/other_module';
import {y} from './module_in_myapp';
Other modules can use initialized logger directly:
// #my/other_module
import {logger} from '#my/logger';
const log = logger('moduleX'); // logger has been initialized
export function x() {
log.debug('some msg');
}
The dependency tree is:
<init>
myapp --+--> logger_init ------------> #my/logger
| <use> ↑
+--> module_in_myapp -----------+
| <use> |
+--> #my/other_module ----------+
Why I don't adopt the way that add a wrapper module which init and return a logger (as Bergi's answer) is because the modules uses logger could be reusable modules not in my application.
Try to provide some entry points to your xxx.js and yyy.js modules or even make them as functions.
// main.js
import {logger} from '#my/logger';
import * as xxx from './xxx';
logger.init({...});
xxx.run();
// xxx.js
import {logger} from '#my/logger';
export function run () {
logger.debug('In xxx');
}
You could have each module that has a dependency on some initialisation routine return a function that can be executed manually to invoke its functionality, rather than expose that functionality as an immediate side-effect of importing it. For example, in xxx.js:
// xxx.js
export default function (log) {
log('xxx');
}
Then place all initialisation operations within an entry file and invoke the modules defined in the above way after these operations are complete:
// main.js
import xxx from './xxx';
import {logger} from '#my/logger';
logger.init({...});
xxx(logger);
But what if I really need to access it during module loading?
Please see the amended code examples above. Pass the instance of logger to the function exported by each module.
A logger, which need to be initialized before used. What if I need to access it during module loading?
Make an extra module with an initialised logger:
// main.js
import {x as xxx} from './moduleX';
import {y as yyy} from './moduleY';
// logger_ready.js
import {logger} from '#my/logger';
logger.init({…});
export default logger;
// moduleX.js
import logger from 'logger_ready';
const log = logger('moduleX'); // logger is initialized here!
export function x() {
log.debug('some msg');
}
After experiments, found that the BroadcastChannel api is perfect to share data or events easily between different es6 modules, tabs, workers, frames...
We can pass objects or arrays by using json stringify there too:
To send datas:
new BroadcastChannel('myapp_section8').postMessage('Hello world!')
Example receiving, from another module:
new BroadcastChannel('myapp_section8').addEventListener('message', function(e){
console.log(e.data)
})
It avoid to use any DOM elements to dispatchEvent, it simplify stuffs a lot!

Categories