How to set up unit tests in sailsjs - sails.js

I cannot run sailsjs unit tests. It seems sails cannot be lifted,
my test (/test/unit/test.js):
var Sails = require('sails');
var app;
before(function() {
console.log('before');
Sails.lift({
log: {
level: 'error'
}
}, function(err, server) {
console.log('lifted');
app = server;
done(err, app);
});
});
// Global after hook
after(function(done) {
app.lower(done);
});
describe('mailer service', function() {
it('should connect to gmail', function() {
console.log(app);
});
});
In my app folder I run: mocha test/unit/test.js
The "app" variable is undefined, console.log('lifted') is not being triggered. What am I doing wrong?

First of all.
You need to call before with a done parameter :
before(function(done){...})
Does your app lift succesfully when you run it with ?
sails lift

Related

mocha test for sails hook that depends on a sails app

I'm trying to write a mocha test for a sails installable hook (myhook) that is dependent on a particular sails app (myapp). I'd like the bootstrap.test.js to lift myapp with myhook. Thus, I have myapp a devDependency in myhook project.
My bootstrap.test.js has something like this:
var myapp = require('myapp');
// put it in global (special case) for npm test only
global.thehook = require('../api/hooks/myhook/index');
before(function(done) {
this.timeout(10000);
console.log("Bootstrap lifting sails...");
myapp.lift({
hooks: {
"myhook": global.thehook,
"grunt": false
},
log: {level: "error"},
}, function(err) {
if (err) return done(err);
// here you can load fixtures, etc.
done(err, sails);
});
});
after(function(done) {
myapp.lower(done);
});
Thinking .lift() and .lower would apply to the sails app. But, that doesn't seem to be the case.
How do I make this work?
You will need to use the sails dependency in place of myapp.
var sails = require('sails');
before(function(done) {
sails.lift({
// test configuration
}, function (error) {
// ...
done();
});
});
after(function(done) {
sails.lower(function (error) {
//...
done();
});
})
The sails dependency starts in the root of the project directory and will lift the application, so there's no need to require app.js for lifting the app.

React + Sails + Socket.io

This is quite a broad question, however I currently have a Sails API server and a React Front-end (Standalone).
Note: The React Front-End is NOT part of Sails
I'm trying to get to grips with sockets, so I figured I would start simple. I want to achieve the following:
User visits my website (React)
React opens a socket and connects to Sails
Sails streams the data from within a function/model
React updates when new data is added to the model
I semi understand how this works using Express and React, however I cannot get my head around how Sails implements their version of WebSockets on top of Sockets.io.
What I've done is install the sockets.io-client within React, and then trying to use sails.sockets inside Sails.
This is what I currently have:
React Component NB: I don't think this is correct at all
componentDidMount =()=> {
this.getSessionData();
UserStore.listen(this.getSessionData);
Socket.emit('/listSessions', function(data){
console.log(data);
})
}
Sails Function (listSessions)
listSessions: function(req, res) {
Session.find({ where: {visible: true}, sort: 'createdAt DESC'},
function(err, sessions){
if(req.isSocket){
Session.watch(req.socket);
console.log('User subscribed to ' + req.socket.id);
}
if(err) return res.json(500, {
error: err,
message: 'Something went wrong when finding trades'
});
return res.json(200, {
sessions: sessions,
});
})
},
Sails Function (createSession) Trying to use publishCreate to use in conjunction with Session.watch in the above function
createSession: function(req, res){
var token = jwt.sign({
expiresIn: 30,
}, 'overwatch');
Session.create({
username: req.body.username,
platform: req.body.platform,
lookingFor: req.body.lookingFor,
microphone: req.body.microphone,
gameMode: req.body.gameMode,
comments: req.body.comments,
avatar: null,
level: null,
hash: token,
competitiveRank: null,
region: req.body.region,
visible: true,
}).exec(function(err, created){
Session.publishCreate(created);
if(err) {
console.log(err);
return res.send({
error: err,
message: 'Something went wrong when adding a session',
code: 91
})
}
if(req.isSocket){
Session.watch(req.socket);
console.log('User subscribed to ' + req.socket.id);
}
return res.send({
session: created,
code: 00,
})
});
},
Both of the Sails functions are called using POST/GET.
I'm completely stumped as where to go with this, and it seems to documentation or explanation on how to get this working is limited. All the Sails documentation on Sockets seems to relate to using Sails as a front-end and server
OK so I managed to solve this:
Simply put:
Within React, I had to include https://github.com/balderdashy/sails.io.js/tree/master
Then within my React component I did:
componentDidMount =()=> {
io.socket.get('/listSessions',(resData, jwres) => {
console.log('test');
this.setState({
sessions: resData.sessions,
loaded: true,
})
})
io.socket.on('session', (event) => {
if(event.verb == 'created') {
let sessions = this.state.sessions;
sessions.push(event.data);
this.setState({
sessions: sessions
})
} else {
console.log('nah');
}
});
}
This makes a virtual get request to Sails using Socket.io, and sets the response in state. It also watches for updates to the 'session' connection and updates the state with these updates meaning I can update a list in real time
Within my Sails controller I have:
listSessions: function(req, res) {
if(req.isSocket){
Session.find({ where: {visible: true}, sort: 'createdAt DESC'},
function(err, sessions){
Session.watch(req.socket);
if(err) return res.json(500, {
error: err,
message: 'Something went wrong when finding trades'
});
return res.json(200, {
sessions: sessions,
});
})
}
},
The Session.watch line listens for updates via publishCreate on the model which is found in my model as follows:
afterCreate: function(message, next) {
Session.publishCreate(message);
next();
},
Adding to answer by #K20GH , add the following to my "index.js" in React to help get sails.io.js from the CDN :
const fetchJsFromCDN = (src, externals = []) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.setAttribute('src', src);
script.addEventListener('load', () => {
resolve(
externals.map(key => {
const ext = window[key];
typeof ext === 'undefined' &&
console.warn(`No external named '${key}' in window`);
return ext;
})
);
});
script.addEventListener('error', reject);
document.body.appendChild(script);
});
};
fetchJsFromCDN(
'https://cdnjs.cloudflare.com/ajax/libs/sails.io.js/1.0.1/sails.io.min.js',
['io']
).then(([io]) => {
if (process.env.NODE_ENV === 'development') {
io.sails.url = 'http://localhost:1337';
}
});
Once you have this, you'll be able to use the HTTP type GET, PUT, POST and DELETE methods. So here you can do:
componentDidMount =()=> {
io.socket.get('/listSessions',(resData, jwres) => {
console.log('test');
this.setState({
sessions: resData.sessions,
loaded: true,
})
})
io.socket.on('session', (event) => {
if(event.verb == 'created') {
let sessions = this.state.sessions;
sessions.push(event.data);
this.setState({
sessions: sessions
})
} else {
console.log('Not created session');
}
});
}
And you can do the required setup in sails for the models of sessions as suggested above

Disable bootstrap.js when run unit tests

I have sails.on('lifted',...) in config\bootstrap.js.
How to not run this when running mocha unit tests?
While running tests, you can load sails instead of lifting it.
var Sails = require('sails').Sails;
before(function (done) {
new Sails().load(
{}, // your configuration
done
);
});
after(function (done) {
if (sails) { return sails.lower(done); }
return done();
});

How to seed dev database in Sails.js in a reproducible way

I'm looking for a best way to seed my development database in sails js.
In rails I would just use the seeds.rb file but even without that I could use a rake task.
With sails I am unsure of how I could do this outside of manually doing it with sails console.
Please note that solutions which add logic to config/models and the models themselves for production seeding are not what I am looking for. I don't want these records to exist in production.
You can seed your database in the config/bootstrap.js file.
To seed it for a particular environment, what I usually do is:
// config/bootstrap.js
module.exports.bootstrap = function (cb) {
if(process.env.NODE_ENV !== 'development')
return cb();
// Do whatever you want
// And don't forget to...
return cb();
};
And to drop the database each time during the Sails lifting:
// config/env/development.js
module.exports = {
models: {
migrate: 'drop'
}
};
You can use test framework, like Mocha. At your development mode, switch your table name to development table. Here is step by step:
Install mocha with npm install mocha --save-dev
Create test/boostrap.test.js and fill with (configure as your needs), look at my configured connections, it'll override default connections at config.
var Sails = require('sails'),
sails;
before(function (done) {
Sails.lift({
log : {
level: 'error'
},
connections: {
mongodbServer: {
database: 'table_test'
}
},
models : {
migrate: 'drop'
}
}, function (err, server) {
sails = server;
done(err, sails);
});
});
after(function (done) {
// here you can clear fixtures, etc.
sails.lower(done);
});
Create another file for seeding your data, for example create test/inject/seed.js and fill with something like.
describe('data seeding', function(){
it('should seed data', function(done){
sails.models.someModel
.create({
name: 'Some Name'
})
.then(function(result){
done();
})
.catch(done);
});
});
Add this at your package.json under "scripts" key.
"test": "_mocha test/bootstrap.test.js test/inject/**/*.inject.js --no-timeouts"
Run it with npm test to seed your data.
If you need to use it at development mode, when you run sails lift, edit your config/env/development.js and add something like this.
module.exports = {
connections: {
mongodbServer: {
database: 'table_test'
}
}
};
Now your sails lift will use table_test instead of production table, so your production table will be clean.

Testing Angular $resource with external service

I'm trying to make some basic tests on REST requests I'm doing using Angular $resource.
The service code works just fine.
'use strict';
angular.module('lelylan.services', ['ngResource']).
factory('Device', ['Settings', '$resource', '$http', function(Settings, $resource, $http) {
var token = 'df39d56eaa83cf94ef546cebdfb31241327e62f8712ddc4fad0297e8de746f62';
$http.defaults.headers.common["Authorization"] = 'Bearer ' + token;
var resource = $resource(
'http://localhost:port/devices/:id',
{ port: ':3001', id: '#id' },
{ update: { method: 'PUT' } }
);
return resource;
}]);
I'm using the Device resource inside a directive and it works. The problems comes out
when I start making some tests on the services. Here is a sample test where I mock the
HTTP request using $httpBackend and I make a request to the mocked URL.
Unluckily it does not return anything, although the request is made. I'm sure about this
because if a request to another URL is made, the test suite automatically raises an error.
I've been spending lot of time, but no solutions. Here the test code.
'use strict';
var $httpBackend;
describe('Services', function() {
beforeEach(module('lelylan'));
beforeEach(inject(function($injector) {
var uri = 'http://localhost:3001/devices/50c61ff1d033a9b610000001';
var device = { name: 'Light', updated_at: '2012-12-20T18:40:19Z' };
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET(uri).respond(device)
}));
describe('Device#get', function() {
it('returns a JSON', inject(function(Device) {
device = Device.get({ id: '50c61ff1d033a9b610000001' });
expect(device.name).toEqual('Light');
}));
});
});
As the device is not loaded this is the error.
Expected undefined to equal 'Light'.
Error: Expected undefined to equal 'Light'.
I've tried also using the following solution, but it doesn't get into the function
to check the expectation.
it('returns a JSON', inject(function(Device) {
device = Device.get({ id: '50c61ff1d033a9b610000001' }, function() {
expect(device.name).toEqual('Light');
});
}));
Any suggestion or link to solve this problem is really appreciated.
Thanks a lot.
You were very close, the only thing missing was a call to the $httpBackend.flush();. The working test looks like follows:
it('returns a JSON', inject(function(Device) {
var device = Device.get({ id: '50c61ff1d033a9b610000001' });
$httpBackend.flush();
expect(device.name).toEqual('Light');
}));
and a live test in plunker: http://plnkr.co/edit/Pp0LbLHs0Qxlgqkl948l?p=preview
You might also want to check docs for the $httpBackend mock.
In later versions of angular, I'm using 1.2.0rc1 you also need to call this within a $apply or call $digest on a scope. The resource call isn't made unless you do something like this:
var o, back, scope;
beforeEach(inject(function( $httpBackend, TestAPI,$rootScope) {
o = TestAPI;
back = $httpBackend;
scope = $rootScope.$new();
}));
it('should call the test api service', function() {
back.whenGET('/api/test').respond({});
back.expectGET('/api/test');
scope.$apply( o.test());
back.flush();
});