Cannot access variables outside scope in browser context - javascript

Using protractor for angular e2e tests. I have a scope problem with the addMockModule. The spec states:
The JavaScript to load the module. Note that this will be executed in
the browser context, so it cannot access variables from outside its
scope.
https://angular.github.io/protractor/#/api?view=Protractor.prototype.addMockModule
browser.addMockModule('modName', function() {
angular.module('modName', []).value('foo', 'bar');
});
Problem: I am trying to pass another function Mockr to my mock module. I want to set and get a mock id.
steps.js
var mockData = require('../../mocks/data.js');
var Mockr = require('../../mocks/mockr.js');
var steps = function() {
this.Before(function(scenario, done) {
browser.addMockModule('httpBackEndMock', mockData.module, Mockr );
done();
});
};
module.exports = steps;
data.js
exports.module = function ( Mockr ) {
angular.module('httpBackEndMock', ['ngMockE2E'])
.run(function($httpBackend) {
// cannot access Mockr here...
// since this is in browser context scope,
// how can I access Mockr???
// like: Mockr.getMockId();
});
};
mockr.js
module.exports = {
id: 5,
setMockId: function(id) {
module.exports.id = id;
},
getMockId: function() {
return module.exports.id;
}
};

Related

Jasmine and angular mocks : mocking a service that handles local storage

I have one service called wd$cache, that is basically a wrapper for localStorage.setItem and get.item.
Now I'm trying to test a controller that uses that service to achieve a certain result. The main problem is that I have an IF statement that gets triggered only if you have localstorage set already which is driving me nuts! (we are doing TDD here)
SERVICE
(function () {
angular
.module('hub')
.controller('promotionNotificationCtrl', promotionNotificationCtrl);
promotionNotificationCtrl.$inject = [
'hub$promotions',
'hub$client',
'wd$cache'
];
function promotionNotificationCtrl(
hub$promotions,
hub$client,
wd$cache) {
var vm = this;
activate();
//////////
function activate () {
hub$promotions.get(hub$client.brand, hub$client.subbrand).success(function (data) {
if (!wd$cache.get('hub$notification')) {
wd$cache.add('before', 123);
} else {
wd$cache.add('after', 321);
}
});
}
}
})();
TEST
describe('The promotion notification controller', function () {
var controller,
hub$client,
$httpBackend,
wd$cache,
mockData = [{
"foo": "bar"
},
{
"faa": "boo"
}];
beforeEach(module('hub'));
beforeEach(module('wired.core'));
beforeEach(module(function ($provide) {
hub$client = {
brand: 'bw',
subbrand: 'plus'
};
wd$cache = {
add: function () {
},
get: function () {
}
};
$provide.value('hub$client', hub$client);
$provide.value('wd$cache', wd$cache);
spyOn(wd$cache, 'add');
}));
beforeEach(inject(function ($controller, _$httpBackend_, _hub$promotions_) {
controller = $controller('promotionNotificationCtrl');
$httpBackend = _$httpBackend_;
hub$promotions = _hub$promotions_;
// request
$httpBackend.expectGET("/umbraco/api/promotions/get/?brand=bw&lang=en&subbrand=plus").respond(200, mockData);
$httpBackend.flush();
}));
it('should attempt to add a cache with a "before" key if no previous "hub$notification" cache was found', function () {
expect(wd$cache.add).toHaveBeenCalledWith('before', 123); //WORKING
})
it('should attempt to add a cache with a "after" key if a previous "hub$notification" cache was found', function () {
localStorage.setItem('hub$notification');
wd$cache.add('hub$notification');
expect(wd$cache.add).toHaveBeenCalledWith('after', 123); // NOT WORKING
// CANT GET THROUGH THE IF STATEMENT
})
});
Basically I can never get to 'Test Cases' after BeforeEach block, whatever I do. I've tried everything, since mocking it to use actual storage.
Any ideas?
You can provide a mock implementation that is already filled with some data:
var cache = {};
beforeEach(module(function ($provide) {
// ...
wd$cache = {
add: function (key, value) {
cache[key] = value;
},
get: function (key) {
return cache[key];
}
};
// add initial data here or in the individual tests, e.g.
// ...
}));
To set up the cache properly for a specific testcase you can use the cache field like this:
cache['hub$notification'] = 'whatever value makes sense here';
Of course you can also do this in beforeEach.
Currently you are trying to do it like this:
wd$cache.add('hub$notification');
expect(wd$cache.add).toHaveBeenCalledWith('after', 123);
This is problematic for two reasons:
You are not updating the cache because you are spying on the add method without .andCallThrough(). You should fix this (add .andCallThrough() after spy creation) otherwise updates from the controller will not affect the cache.
The spy records your call instead. You don't want this for setup code because it makes subsequent assertions more complicated.

node.js unit testing mock dependency

I have a question on using proxyquire (or any other suggestions on how to test the following code)
If i have the following file to test:
var path = require('path');
module.exports = function (conf) {
var exported = {};
exported.getIssue = function (issueId, done) {
...
...
};
return exported;
};
How do i pass in the 'conf' variable while using proxyquire to mock 'path; var?
Is there any other way to do this if not using proxyquire?
You simply have to pass conf variable to the module/function required via proxyquire. Proxyquire has meaning to to do the same CommonJs require stuff, but with the possibility of mocking and stubing some modules. So you should act the same.
var pathMock = {
someMethod = function() {}
};
var confMock = {
someMethod = function() {}
};
spyOn(pathMock, 'someMethod').and.callThrough();
spyOn(confMock, 'someMethod').and.callThrough();
module = proxyquire('../path/to/module', {
path: pathMock
});
it('do some test', function(done) {
module(conf).getIssue();
expect(pathMock.someMethod).toHaveBeenCalled;
expect(confMock.someMethod).toHaveBeenCalled;
});

Node JS call a "local" function within module.exports

How do you call a function from within another function in a module.exports declaration?
I have MVC structure node js project and a controller called TestController.js. I want to access method within controller, but using this keyword gives below error:
cannot call method getName of undefined
"use strict"
module.exports = {
myName : function(req, res, next) {
// accessing method within controller
this.getName(data);
},
getName : function(data) {
// code
}
}
How do I access methods within controller?
I found the solution :-)
"use strict"
var self = module.exports = {
myName : function(req, res, next) {
// accessing method within controller
self.getName(data);
},
getName : function(data) {
// code
}
}
You can access the getName function trough module.exports. Like so:
"use strict"
module.exports = {
myName : function(req, res, next) {
// accessing method within controller
module.exports.getName(data);
},
getName : function(data) {
// code
}
}
Maybe you could do it like this. It reduce nesting. And all your export is done at the end of your file.
"use strict";
var _getName = function() {
return 'john';
};
var _myName = function() {
return _getName();
};
module.exports = {
getName : _getName,
myName : _myName
};
If you want to use the function locally AND in other files...
function myFunc(){
return 'got it'
}
module.exports.myFunc = myFunc;
I know the answer is already accepted, but I feel the need to add my two cents on the issue.
Node modules have a "Singletonic" nature, when inside the module, you are the module.
In my opinion, at least design pattern wise, inner module methods can be accessed more cleanly, without the need for this or a copy of self for that matter.
Using this, could be dangerous, if one happens to send the separate methods around and forgets to use .bind.
Using a copy of self, is redundant, we already are inside a Singleton behaving module, why keep a reference to yourself when you can avoid that?
Consider these instead:
Option 1
// using "exports."
exports.utilityMethod = (..args) => {
// do stuff with args
}
exports.doSomething = (someParam) => {
// this always refers to the module
// no matter what context you are in
exports.utility(someParam)
}
Option 2
// using module.exports
const utility = (..args) => {
// do stuff with args
}
const doSomething = (someParam) => {
// Inside the module, the utility method is available
// to all members
utility(someParam)
}
// either this
module.exports = {
utility,
doSomething,
}
// or
module.exports = {
customNameForUtility: utility,
customNameForDoSomething: doSomething
}
This works the same for es6 modules:
Option 1 (ES6)
export const utilityMethod = (..args) => {
// do stuff with args
}
export const doSomething = (someParam) => {
// this always refers to the module
// no matter what context you are in
utility(someParam)
}
Option 2 (ES6)
const utility = (..args) => {
// do stuff with args
}
const doSomething = (someParam) => {
// Inside the module, the utility method is available
// to all members
utility(someParam)
}
export default {
doSomething,
utility
}
// or
export {
doSomething,
utility
}
Again, this is just an opinion, but it looks cleaner, and is more consistent across different implementations, and not a single this/self is used.

ES6: Save variable into module scope

I'd like to know if and how it's possible to save the following config into my modules scope (think of the block below as one module):
var config = null;
var mySingleton = {
init: function(config) {
config = config; // Naming conflict!
},
printConfig: function() {
console.log(config); // Undefined!
}
};
export { mySingleton };
Unfortunately, a naming conflict occurs. I'd need something like module.config = config;.
Note: I'm using the es6-module-transpiler.
No. It's a simple variable, and not a global one (where it would be accessible as a property of the global object), so it just is shadowed and not available "namespaced".
You need to use a different name to resolve conflicts, which should be trivial and will cause no effects outside the module:
var config = null;
var mySingleton = {
init: function(conf) {
config = conf;
},
printConfig: function() {
console.log(config);
}
};
export { mySingleton };

How is it posible to set a var property from within a function defined in that var?

I have a question regarding definitions of factories in AngularJS. I am unsure if this is a Javascript doubt or AngularJS, but I believe it's Angular.
Say I have a factory definition like this:
angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000";
var service = {
// our factory definition
user: {},
setName: function(newName) {
service.user['name'] = newName;
},
setEmail: function(newEmail) {
service.user['email'] = newEmail;
},
save: function() {
return $http.post(backendUrl + '/users', {
user: service.user
});
}
};
return service;
});
How is it possible that the function setName is able to set service.user['email'], if service is actually defining setName itself?
This is basic javascript. Forget about the factory for a minute, only consider definition of service.
var service = {
//service will have an empty user js object
user: {},
//a key setName with the value as a function
//which sets a key value pair in user defined above
//service = { user: {name: 'Tony'} }
setName: function(newName) {
service.user['name'] = newName;
},
//a key setEmail with the value as a function
//which sets a key value pair in user defined above
//(consider setName has been called already)
//service = { user: {name: 'Tony', email: 'tony#stark.com'} }
setEmail: function(newEmail) {
service.user['email'] = newEmail;
},
//a key save with the value as a function
//which saves
save: function() {
//return something from a call mimicing $http.post
//return $http.post(backendUrl + '/users', { user: service.user });
}
}
Then, return or use service which has become an API by itself.
Check out dev tool console for log in this FIDDLE
setName function is going to be called by some other code like this,
User.setName("newname "); when this is called the service already defined and it's about setting new value to a property.
and you could use this keyword to make the code more meaningful,
setName: function(newName) {
this.user['name'] = newName;
}
I see one issue with your code, even though you have defined setName function you have the user variable is accessible to outside of the factory,now you can do like this as well,
User.user['name'] = newName;
I think what you need to do is make user a private variable,
angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000";
var user={};
var service = {
// our factory definition
setName: function(newName) {
user['name'] = newName;
},
setEmail: function(newEmail) {
user['email'] = newEmail;
},
getUser:function(){
return user;
},
save: function() {
return $http.post(backendUrl + '/users', {
user: service.user
});
}
};
return service;
});
What you're describing is a behavior of JavaScript's closures.
Basically, a function has access to the variables outside of the function's scope. If those variables change, it's changed everywhere.
When the functions are created, service is still undefined
The functions create a closure around the service variable
service is then assigned to be the object
All the functions are now pointing to the service object
Here's a way to visualize this:
var service = null;
var getService = function() { return service; };
service = 5;
getService(); // returns 5
service = "service can change";
getService(); // returns "service can change";

Categories

Resources