AngularJS Protractor and Socket.IO - node.js

When setting up an E2E test with my existing angular app, I am having trouble getting starting since I am using socket.io in my application. Protractor can't find the socket.io files that are typically served by the node module since it's not part of my personal code.
I am getting the error:
UnknownError: Error Message => 'Can't find variable: io'
On the angular side:
angular.module('socket.io', []).factory(function($rootScope) {
var socket = io.connect(); // I believe the error is being thrown from here
return {
on: function (eventName, callback) {
socket.on(eventName, function () {...});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {...});
}
};
});
I believe that is because it's typically served by the node module:
<script type="text/javascript" src='/socket.io/socket.io.js'></script>
vs my socket.io factory shown above
<script type="text/javascript" src='js/services/socket-service.js'></script>
Is there a way to solve this without having the copy the socket.io.js file in to the client codebase?

Make sure that the file socket.io.js is being included before socket-service.js. You can either do thus manually or use a module loader such as requirejs (http://www.requirejs.org)

Related

socket.io 1.0.x not compatible with require.js?

Any time I try to load socket.io 1.0.x after require.js, it produces
Uncaught ReferenceError: io is not defined
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.0.6.js"></script>
<script type="text/javascript">
var socket = io('http://localhost');
</script>
But if I put socket.io before require.js, it doesn't produce any error:
<script src="https://cdn.socket.io/socket.io-1.0.6.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script type="text/javascript">
var socket = io('http://localhost');
</script>
This of course means that socket.io cannot be loaded by require.js this way:
requirejs.config({
paths: {
'socketio' : ['https://cdn.socket.io/socket.io-1.0.6'],
},
shim: {
'socketio': {
exports: 'io'
}
}
});
require(['socketio'], function(io){
console.log(window.io); //undefined
})
Socket.io 0.x
with socket.io 0.x you needed in to export io in the require.js config:
shim: {
'socket.io-client': {
exports: 'io'
}
}
and your js file:
define(['socket.io-client'], function () {
// io is exported here
var socket = io.connect ...
}
Socket.io 1.x
with socket.io 1.x, no need to export io in the require.js config file
and your js file:
define(['socket.io-client'], function (io) {
// io an argument of the function
var socket = io.connect ...
}
But in both cases, you have to use the io object in the define so that it is loaded correctly by require.js when you need it.
Also note that you need socket.io-client instead of socket.io in the browser
RequireJS creates a require and define set of methods... the socket.io js file sees this, and defines itself.
Include socket.io first or Use AMD
I specifically wanted the bower packages (ng-socket), and socket.io-client to cooperate via RequireJS. At first ng-socket errored because io was undefined, even with shimmed deps.
So, I added a startSocketIo.js file:
define([
'socket.io-client'],function(io) {
window.io = io;
});
and added a deps section to the root of my require config file:
deps: [
'./startSocketIo'
],
I see this as analogous to adding via a script tag and no better for performance, but it allowed me to keep using unmodified bower packages and using requirejs for all my js.

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.)

Node is not resolving a client side module

I have a Node application where I want to use socket.io to communicate data to a client where it is displayed by smoothie. I have both packages installed (via NPM) on two different node environments and in both cases in the node_modules sub-directory of my project. One of the environments is the BeagleBone Black and the other is the Cloud9 IDE environment. In both cases the socket.io module resolves and works fine but no combination of path names gets the smoothie module to resolve (which I can get to work if I just pull it from GitHub directly).
Here are the relevant bits of the server side code for the Cloud9 IDE:
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(process.env.PORT, process.env.IP);
function handler (req, res) {
fs.readFile(__dirname + '/NotWorking.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
.
.
.
Here are the relevant bits from the client side:
<!DOCTYPE html>
<html>
<head>
<script src="smoothie/smoothie.js"></script>
<script src="socket.io/socket.io.js"></script>
<script>
var line1 = new TimeSeries();
var line2 = new TimeSeries();
var socket = io.connect('http://demo-project.wisar.c9.io/');
socket.on('news', function (data) {
for (var property in data) {
dataPoint = data[property];
}
line1.append(new Date().getTime(), dataPoint);
line2.append(new Date().getTime(), 40);
socket.emit('my other event', { my: dataPoint });
});
</script>
.
.
.
As I said, both modules are located in the node_modules sub directory of the project directory where the above scripts live. The node documentation describes how includes are supposed to be resolved (http://nodejs.org/api/modules.html#modules_all_together) and I think that I can follow the path to how it resolves the link to socket.io by way of the index.js route...but it also works when I put a "/" in front which I can not find a path for. No permutation or combination of paths makes the smoothie module resolve. smoothie, btw, is a small charting application that can be found in npm under that name.
Any help would be appreciated.
If your current file is in the same directory as node_modules, then to load smoothie try this path in src of script tag:
./node_modules/smoothie/smoothie.js
The path smoothie/smoothie.js is not giving the location of smoothie.js, which lies in node_modules/smoothie/smoothie.js. This worked for me, I hope this works for you.

phantomjs and requirejs

codes in file main.js is like this:
phantom.injectJs("libs/require-1.0.7.js");
require.config(
{
baseUrl: ""
}
);
require([], function(){});
when i run "phantomjs main.js" in the commandline, requirejs doesn't work well in the main.js. I know how to use requirejs in the page running in the browser(including phantomjs' way: page.open(url, callback)), but not like above. I tries using requirejs like the main.js, it is a popular problem, i think. Thank you!
I just struggled for some time. My solution is not clean, but it works, and I'm happy with that due to the unfinished api documentation from phantomjs.
Wordy explanation
You need three files. One is your amd phantomjs test file which I'll call "amd.js". The second is your html page to load which I'll name "amd.html". Finally the browser test which I called "amdTestModule.js".
In amd.html, declare your script tag per normal:
<script data-main="amdTestModule.js" src="require.js"></script>
In your phantomjs test file, this is where it gets hacky. Create your page, and load in the 'fs' module. This allows you to open a relative file path.
var page = require('webpage').create();
var fs = require('fs');
page.open('file://' + fs.absolute('tests/amd.html'));
Now since requirejs loads files asynchronously, we can't just pass in a callback into page.open and expect things to go smoothly. We need some way to either
1) Test our module in the browser and communicate the result back to our phantomjs context. Or
2) Tell our phantomjs context that upon loading all the resources, to run a test.
#1 was simpler for my case. I accomplished this via:
page.onConsoleMessage = function(msg) {
msg = msg.split('=');
if (msg[1] === 'success') {
console.log('amd test successful');
} else {
console.log('amd test failed');
}
phantom.exit();
};
**See full code below for my console.log message.
Now phantomjs apparently has an event api built in but it is undocumented. I was also successfully able to get request/response messages from their page.onResourceReceived and page.onResourceRequested - meaning you can debug when all your required modules are loaded. To communicate my test result however, I just used console.log.
Now what happens if the console.log message is never ran? The only way I could think of resolving this was to use setTimeout
setTimeout(function() {
console.log('amd test failed - timeout');
phantom.exit();
}, 500);
That should do it!
Full Code
directory structure
/projectRoot
/tests
- amd.js
- amdTestModule.js
- amd.html
- require.js (which I symlinked)
- <dependencies> (also symlinked)
amd.js
'use strict';
var page = require('webpage').create();
var fs = require('fs');
/*
page.onResourceRequested = function(req) {
console.log('\n');
console.log('REQUEST');
console.log(JSON.stringify(req, null, 4));
console.log('\n');
};
page.onResourceReceived = function(response) {
console.log('\n');
console.log('RESPONSE');
console.log('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response, null, 4));
console.log('\n');
};
*/
page.onConsoleMessage = function(msg) {
msg = msg.split('=');
if (msg[1] === 'success') {
console.log('amd test successful');
} else {
console.log('amd test failed');
}
phantom.exit();
};
page.open('file://' + fs.absolute('tests/amd.html'));
setTimeout(function() {
console.log('amd test failed - timeout');
phantom.exit();
}, 500);
amd.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script data-main='amdTestModule.js' src='require.js'></script>
</body>
</html>
amdTestModule.js
require([<dependencies>], function(<dependencies>) {
...
console.log(
(<test>) ? "test=success" : "test=failed"
);
});
console
$ phantomjs tests/amd.js
amd test successful
you are misunderstanding webpage.injectJs()
it's for injecting scripts into the page you are loading, not into the phantomjs runtime environment.
So using .injectJs() is making requirejs load up into your page, not into phantomjs.exe.
That said, phantomjs's runtime environment has an aproximation of commonjs. RequireJs will not run on there by default. If you felt especially (VERY) motivated, you could attempt porting the require-shim made for nodejs, but it doesn't work out of the box, and would require an incredibly deep understanding of the runtimes. for more details: http://requirejs.org/docs/node.html
a better idea:
probably you should make sure you have commonjs versions of your javascript you wish to run. i personally write my code in typescript so i can build for either commonjs or amd. i use commonjs for phantomjs code, and amd for nodejs and browser.

how to run nodejs flatiron/director example

I tried to run the https://github.com/flatiron/director#client-side example to get familiar with director.js.
I am not able to setup the flatiron module on the client-side.
In my html page (say, <my_project>/page.html) I replaced the location of director.js with
a location of its counterpart from my project:
<my_project>/node_modules/flatiron/node_modules/director/lib/director.js
Upon loading the <my_project>/page.html page in the browser
I got errors: export and Router not defined.
First idea: After all, on the browser side there is no nodejs...
Ok, I thought that browserify could help me with it.
I generated a single 'browser-side' bundle (was it necessary?):
my_project> node node_modules/browserify/bin/cli.js node_modules/flatiron/node_modules/director/lib director.js -o cs_director.js
and I used it in the line: <script src="cs_director.js"></script>
The problem is that the error
Uncaught ReferenceError: Router is not defined
(anonymous function)
still appears so I guess the whole example will not work.
I am new to node/js and I am not sure if it makes sens what I have done in my case described above...
Does anybody how to solve it?
Or generally, how to use 'isomorphic' stuff on a browser-side?
The html examples on Github just refer to the same .js files
as server-side examples ...
Can you recommend any tutorials, examples?
Thanks,
-gvlax
You can find a browser-specific build of director here which has all of the server code stripped away.
Thanks DeadDEnD,
Now it works like a charm!
I have no idea how I could missed that info in readme ... I read the manual first, I swear:-)
Here is my sample code:
<!html>
<html>
<head>
<script src="director-1.0.7.min.js"></script>
<script>
var author = function () { console.log("author called.");},
books = function () { console.log("books called."); },
viewBook = function(bookId) { console.log("viewBook called."); };
var routes = {
'/author': author,
'/books': [books, function() { console.log("anonymous fun called."); }],
'/books/view/:bookId': viewBook
};
var router = Router(routes);
router.init();
</script>
</head>
<body>
Click me to call two functions at a time.
</body>
</html>
--gvlax

Resources