Calling functions located in separate files with Intern - require

Using intern, I have the following directory structure:
/intern
/tests
globals.js
pageElements.js
/functional
/site1
site1test1.js
/site2
site2test1.js
/page_objects
loginPage.js
What I want to do is store common functions in the page_objects directory for various pages, such as loginPage.js, and call these functions in the actual tests like site1test1.js etc. I've tried many various approaches, the latest of which is based on a solution posted here: Use helper modules for repeated, ordered tasks when functional testing in intern. With this approach I'm seeing "TypeError: Object # has no method 'setFindTimeout'". I'm very new to intern, and quite new to node.js, so please pardon my ignorance. I just would like to get input on the best approach to take here.
site1test1.js looks like this:
define([
'intern!object',
'intern/chai!assert',
'require',
'intern',
'intern/dojo/node!../globals.js',
'intern/dojo/node!./page_objects/loginPage.js'
], function (registerSuite, assert, require, intern, globals, loginPage) {
registerSuite({
name: 'index',
'login': function () {
var current_env = intern.config.environment;
var user = globals.getData(current_env).USER1;
var url = globals.getData(current_env).url;
return this.remote
.get(url)
.then(loginPage.login(user, globals.PASSWORD));
}
});
});
My loginPage.js currently looks like this:
var pageElements = require('../../pageElements');
var globals = require('../../globals')
module.exports = {
login: function(email, password) {
return new this.constructor(this.session)
.setFindTimeout(globals.TIMEOUT)
.findById(pageElements.TEXTFIELD_ID_EMAIL)
.click()
.clearValue()
.type(email)
.end()
.findById(pageElements.TEXTFIELD_ID_PASSWORD)
.click()
.clearValue()
.type(password)
.end()
.findById(pageElements.BUTTON_ID_LOGIN)
.click()
.end()
}
};

I was able to resolve the issue by AMDifying the loginPage module as follows:
define([
'intern!object',
'intern/chai!assert',
'require',
'intern',
'../../globals.js',
'../../pageElements.js'
], function (loginPage,
assert,
require,
intern,
globals,
pageElements) {
var loginPage = {
login : function (email, password) {
return this
.setFindTimeout(globals.TIMEOUT)
.findById(pageElements.TEXTFIELD_ID_EMAIL).click()
.clearValue()
.type(email)
.end()
.findById(pageElements.TEXTFIELD_ID_PASSWORD).click()
.clearValue()
.type(password)
.end()
.findById(pageElements.BUTTON_ID_LOGIN).click()
.end()
}
};
return loginPage;
});
I then can call this in site1test1.js as follows:
return this.remote
.get(url)
.then(function(){ return loginPage.login.call(this, user, globals.PASSWORD); });

I was able to get this to work as follows. (based on tera_incognita's answer)
site1test1.js:
define([
'intern!object',
'intern/chai!assert',
'intern/dojo/node!leadfoot/Command',
'intern/dojo/node!leadfoot/lib/util',
'require',
'intern',
'../page_objects/loginPage.js'
], function (registerSuite, assert, Command, util, require, intern, loginPage) {
registerSuite({
name: 'index',
'login': function () {
var session = this.remote;
var command = new Command(session);
var current_env = intern.config.environment;
var user = 'john_appleseed#apple.com';
var url = 'http://localhost/hello.html';
return command
.get(url)
.then(function(){ return loginPage.login(session, user, 'password1');});
}
});
});
loginPage.js:
define([
'intern!object',
'intern/chai!assert',
'require',
'intern'
], function (loginPage,
assert,
require,
intern) {
var loginPage = {
login : function (session, email, password) {
return session
.findById('email').click()
.clearValue()
.type(email)
.end()
.findById('password').click()
.clearValue()
.type(password)
.end()
.findById('login').click()
.end()
}
};
return loginPage;
});
hello.html:
<html>
Hello!
<form>
Email:<input id="email" type='text' name="email"><br>
Password: <input id="password" type='text' name="password"><br>
<input id="login" type="submit" value="Log in">
</form>
</html>

Here is how I used fat arrow functions in ES6 (typescript) to get the same effect with less typing.
return this.remote
.then( () => { login(this.remote, 'admin', 'password'); })

Related

How to reuse an element inside of a function in protractor page object model?

I am using protractor with Jasmine in windows 10 OS. This is my page for stack over flow login .May I know how to access txt_username inside of clearme: function().
pgstackover.js
//pgstackover.js
'use strict';
module.exports = {
txt_username: element(by.id('email')),
txt_password: element(by.id('password')),
btn_submit: element(by.id('submit-button')),
clearme: function() {
txt_username.isDisplayed();
txt_username.clear();
txt_password.clear()
} ,
go: function() {
browser.driver.ignoreSynchronization = true;
browser.waitForAngularEnabled(false);
browser.driver.manage().window().maximize();
browser.driver.get('https://stackoverflow.com/users/login'); //overrides baseURL
}
This is my spec.js file and it works until clear me function and fails with error Failed: txt_username is not defined
var toDoPage = require('../../../../pages/pgstackover.js');
describe('My first non angular class', function() {
it('should navigate to stack overflow login page', function() {
toDoPage.go();
});
it ('My function', function() {
// browser.driver.get('https://stackoverflow.com/users/login');
toDoPage.txt_username.sendKeys('6');
toDoPage.txt_password.sendKeys('winchester');
var value=toDoPage.btn_submit.getText();
expect(value).toEqual("Log in");
});
it('clear me', function() {
toDoPage.clearme();
});
})
Too make long story short your code does not know what is txt_username so you need to point it out. You are missing .this in your clearme function.
I haven't used a promise manager for ages (since it is obsolete) so here is a working example using async/await:
async clearme() {
await this.txt_username.isDisplayed();
await this.txt_username.clear();
await this.txt_password.clear();
}

How can I mock a function/property of a node module exported as a function using sinon?

I have a service module that is exported as a function. I need to pass a couple of things into it, like a configuration object so it does need to retain this structure. I am trying to stub out a function from the service but can't figure it out. In my app, I have a function that makes an API call that is problematic during testing so I'd like to stub it. (I understand I'd have to write my test differently to handle the async issue)
// myService.js
module.exports = function(config) {
function foo() {
returns 'bar';
}
return {
foo: foo
};
};
// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);
describe('Simple test', function(done) {
it('should expect "something else", function(done) {
var stub = sinon.stub(myService, 'foo').returns('something else');
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
stub.restore();
})
.end(done);
});
});
* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()
The above is not working, and I believe it has to do with the way my service is exporting. If I write the service as below, the stub works fine.
module.exports = {
test: function() {
return 'something';
}
};
But again, I need to be able to pass in information to the module so I would like to keep my modules in the original structure above. Is there a way to stub a function from a module that exports in that manner? I was also looking into proxyquire but not sure if that is the answer.
The reason why your test stub does not work is that the foo function is created every time the module initializer is called. As you discovered, when you have a static method on the module, then you are able to stub.
There are a variety of solutions to this problem-- but the simplest is to expose the method statically.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function foo() {
return 'bar'
}
It's ugly, but works.
What if the foo function has a closure to variables within the service (which is why it lives within the service initializer). Then unfortunately these need to be explicitly passed in.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function(config) {
return function foo() {
return config.bar;
}
}
Now you can safely stub the module.
However, how you are stubbing should be considered unsafe. Only if your test works perfectly does the stub get cleaned up. You should always stub within the before and after (or beforeEach and afterEach) fixtures, such as:
// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');
describe('Simple test', function(done) {
beforeEach(function () {
// First example, above
this.myStub = sinon.stub(myService, foo).returns('something else');
// Second example, above
this.myStub = sinon.stub(myService, foo).returns(function () {
returns 'something else';
});
});
afterEach(function () {
this.myStub.restore();
});
it('should expect "something else", function(done) {
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
})
.end(done);
});
});
There are other options to be able to stub dependencies using dependency injection. I recommend you look at https://github.com/vkarpov15/wagner-core or my own https://github.com/CaptEmulation/service-builder

How can I stub a Promise such that my test can be run synchronously?

I am trying to unit test a module by stubbing one of its dependencies, in this case the UserManager
A simplified version of the module is as follows:
// CodeHandler
module.exports = function(UserManager) {
return {
oAuthCallback: function(req, res) {
var incomingCode = req.query.code;
var clientKey = req.query.key;
UserManager.saveCode(clientKey, incomingCode)
.then(function(){
res.redirect('https://test.tes');
}).catch(function(err){
res.redirect('back');
}
);
}
};
};
I'm stubbing the UserManager's saveCode function which returns a Promise such that it returns a resolved Promise, but when I assert that res.redirect has been called, alas at the time of the assertion res.redirect has not yet been called.
A simplified version of the unit test is:
// test
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var res = {
redirect: function() {}
};
var expectedUrl = 'https://test.tes';
var ch;
beforeEach(function() {
sinon.stub(UserManager, 'saveCode').returns(
new RSVP.Promise(function(resolve, reject){
resolve();
})
);
sinon.stub(res, 'redirect');
ch = CodeHandler(UserManager);
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
});
How can I properly stub the promise such that the method under test behaves synchronously?
I've worked out a solution using sinon-stub-promise.
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var ch;
var promise;
var res = {
redirect: function() {}
};
beforeEach(function() {
promise = sinon.stub(UserManager, 'saveCode').returnsPromise();
ch = CodeHandler(UserManager);
sinon.stub(res, 'redirect');
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
describe('can save code', function() {
var expectedUrl = 'https://test.tes';
beforeEach(function() {
promise.resolves();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
});
});
describe('can not save code', function() {
var expectedUrl = 'back';
beforeEach(function() {
promise.rejects();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
})
});
This works perfectly.
Well, the easiest thing would be not to stub it to run synchronously at all since that might change execution order and use Mocha's built in promises support (or jasmine-as-promised if using jasmine).
The reason is there can be cases like:
somePromise.then(function(){
doB();
});
doA();
If you cause promises to resolve synchronously the execution order - and thus output of the program changes, making the test worthless.
On the contrary, you can use the test syntax:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () => {
return methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
});
});
You can use the even lighter arrow syntax for single lines which makes the test even less verbose:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () =>
methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
);
});
In RSVP, you can't set the scheduler as far as I know so it's quite impossible to test things synchronously anyway, other libraries like bluebird let you do it at your own risk, but even in libraries that let you do it it's probably not the best idea.

How to test stubbed database queries

I have a Sails.Js controller that looks like this
module.exports = {
confirmID: function(req,res) {
var uid = req.params.id;
User.findOne({id:uid}).exec(function(err,user) {
// ...
});
}
}
where User is a sails-postgres model. I have tried testing it with mocha, sinon and supertest with a test like this
describe('Controller', function() {
var sandbox;
before(function() {
sandbox = sinon.sandbox.create();
sandbox.stub(User, 'findOne');
});
after(function() {
sandbox.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,res) {
sandbox.fakes[0].called.should.be.true;
done();
});
});
});
If I leave it at that it errors out because exec is called on undefined, but I can't seem to stub the nested exec method without either errors or the test hanging. Is there a way to stub a series of method calls such as .find().exec()? Or am I best to just leave this to integration tests where I can test it with an actual database?
Assuming that you really want to stub (not just spy) - you want to control what the query resolves to as opposed to simply knowing whether the query was executed. Here's what I'm using to stub sails/waterline query methods. Something like...
var stubQueryMethod = require('stubQueryMethod');
describe('Controller', function() {
before(function() {
stubQueryMethod(User, 'findOne', {
id: 123,
name: 'Fred Fakes'
});
});
after(function() {
User.findOne.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,user) {
user.should.have.property('name', 'Fred Fakes');
done();
});
});
});
});
Source: https://gist.github.com/wxactly/f2258078d802923a1a0d
For people looking for other options to stub or mock waterline models, I've found the following four options:
stubQueryMethod.js gist - https://gist.github.com/wxactly/f2258078d802923a1a0d
model mock gist - https://gist.github.com/campbellwmorgan/e305cc36365fa2d052a7
weaselpecker - https://github.com/ottogiron/weaselpecker
sails-mock-models - https://github.com/ryanwilliamquinn/sails-mock-models
After evaluating each one, I've decided on sails-mock-models because it is easy to understand and seems the most used sails mocking library according to npm: https://www.npmjs.com/package/sails-mock-models
Hope this helps someone!
Update: I'm still using sails-mock-models, and it is quite easy, but there are a few drawbacks such as it fails to return promises that are taken into a q.all(promiseArray).then() call. If I get around to investigating the other options or find a workaround, I will post it here.
This will only work for queries that use exec and it overloads all exec calls so if you try to return an error and you have, say, a controller with a policy out front, and the policy does a database lookup, you'll likely go into error there prior to hitting the controller code you intended to test.... that can be fixed with stub.onCall(x), but it is still a bit precarious.
Warnings aside, here's how I've done this in the past:
var path = require('path');
var sinon = require('sinon');
var Deferred = require(path.join(
process.cwd(),
'node_modules/sails',
'node_modules/waterline',
'lib/waterline/query/deferred'
));
module.exports = function () {
return sinon.stub(Deferred.prototype, 'exec');
};
Assuming you have the following service, MyService:
module.exports.dbCall = function (id, cb) {
Model.findOne(id).exec(function (err, result) {
if (err) {
sails.log.error('db calls suck, man');
return cb(err, null);
}
cb(null, result);
});
};
You can test the error case like so:
before(function () {
stub = databaseStub();
});
afterEach(function () {
stub.reset();
});
after(function () {
stub.restore();
});
it('should return errors', function (done) {
stub.onCall(0).callsArgWith(0, 'error');
MyService.dbCall(1, function (err, results) {
assert.equal(err, 'error');
assert.equal(results, null);
done();
});
});

How to use helpers in a client-side Jade template using Backbone.js?

I'd like to use momentjs (more generally, any other function) as helper in a Jade template, both for the server and the client. On the server (Express.js 3.x), I'm adding it to app.locals, and it works fine:
Server-side
// Locals
app.locals.moment = require('moment');
// Routes
app.get('/', function (req, res) {
res.render('index.jade')
});
app.listen(config.get('server').port);
So using the moment helper works fine when the template (index.jade) is rendered by the server and then served at / as plain HTML.
Client-side
On the client (RequireJS modules system) I'm using Backbone.js along with, again, Jade in order to render views. How can I use moment helper in navbar.jade?
define(
['jade', 'backbone', 'text!views/navbar.jade'],
function (Jade, Backbone, navbarTemplate) {
return Backbone.View.extend({
initialize: function () {
this.template = Jade.compile(navbarTemplate);
},
render: function () {
this.$el.html(this.template(this.model));
return this;
}
});
}
);
EDIT: based on Peter Lyons suggestion, I've wrapped compile function. moment works but foo doesn't (p= moment() vs p= foo(), the latter gives me cannot call function of undefined):
define(['jade', 'lodash', 'moment'], function (Jade, _) {
return {
compile: function(str, options) {
options.locals = _.extend({
moment: require('moment'),
foo: function () { return 'bar'; }
}, options.locals);
return Jade.compile(str, options);
}
};
});
When you invoke the compiled template function in the browser, there's no out of the box app.locals, so you would have to code it.
//customJade.js module
define(function (require, exports) {
var jade = require('jade');
var moment = require('moment');
exports.compile = function(jadeString) {
//build the normal jade template function
var jadeFn = jade.compile(jadeString);
//build a wrapper function with same API
var customFn = function(locals) {
//in the wrapper, add your shared locals as needed
locals.moment = locals.moment || moment;
locals.appName = "Super Happy Fun App!";
//return the result of the real jade template function,
//but with combined locals
return jadeFn(locals);
};
//return your wrapper function instead of the regular jade function
return customFn;
}
});
.
//your backbone view module
define(['customJade', 'jadeTemplateString'], function (customJade, jadeTemplateString) {
//blah blah your View subclass...
template: customJade.compile(jadeTemplateString),
//...
});

Resources