I need to request some json files that contain data I use for testing. I would like to make the request in the setup method, but there is no async method attached to it. When I run the code below, the log inside the test login function gets sent to the console before my logs from the setup method get sent. Is there a way I can tell setup to wait till my calls get completed before running the tests?
define([
'intern!object',
'pages/LoginPage',
'data-objects/DataFetcher'
], function(registerSuite, LoginPage, DataFetcher) {
registerSuite(function() {
var loginId = admin;
var password = test;
var regionData = US;
var loginPage = null;
return {
name: 'Login test',
setup: function() {
// Initialize page objects
loginPage = new LoginPage(this.remote, this.timeout);
// get test data
DataFetcher.getData(Pages.LoginPage).then(function(response) {
logger.info(DataFetcher.generateData(response));
});
DataFetcher.getData(Pages.TablePage).then(function(response) {
logger.info(DataFetcher.generateData(response));
});
DataFetcher.getData(Pages.PersonPage).then(function(response) {
logger.info(DataFetcher.generateData(response));
});
DataFetcher.getData(Pages.BasicInfoPage).then(function(response) {
logger.info(DataFetcher.generateData(response));
});
DataFetcher.getData(Pages.CompanyInfoPage).then(function(response) {
logger.info(DataFetcher.generateData(response));
});
},
login: function() {
logger.log('info', 'Login is ' + loginId + ' Password ' +
password);
return loginPage.load(regionData.BASE_URL)
.login(loginId, password)
.getAccumulatedState();
}
};
});
});
If you return a Promise from the setup function, Intern will wait for it to resolve before starting tests. You can return a Promise.all(...) of all your requests.
Related
So I just started using jest and I am trying to figure out a difficult problem.
I am working on a Strapi plugin and I am trying to test this function
module.exports = {
async index(ctx) {
let verification = {}
// Checks if there is a captcha provider
console.log('strapi' + strapi)
if (!(strapi.config.get('plugin.ezforms.captchaProvider.name') === 'none') && (strapi.config.get('plugin.ezforms.captchaProvider.name'))) {
verification = await strapi.plugin('ezforms').service(strapi.config.get('plugin.ezforms.captchaProvider.name')).validate(ctx.request.body.token)
//throws error if invalid
if (!verification.valid) {
...
The issue is that the strapi object is injected when running strapi and I want to know how I can inject a fake variable into this function.
I've already implemented a fake strapi object in my test and it looks something like this
test('should return captcha error', async function () {
strapi.plugin().service().validate = jest.fn(function () {
return {
error: {
valid: false,
message: 'Unable to verify captcha',
code: 500
}
}
})
let result = await submitController.index(ctx)
expect(result).toEqual(500)
My issue right now is that when running index() it doesn't have a reference to the Strapi object
describe('Submit Controller', function () {
let strapi
beforeAll(async function () {
strapi = {
...
plugin: function () {
return {
service: function () {
return {
validate: function () {
return {
error: {
valid: false,
message: 'Unable to verify captcha',
code: 500
}
}
}
}
}
}
}
}
})```
I reference in the test file but the `index()` function doesn't have access
[![screenshot][1]][1]
How can I inject my fake Strapi object into the index() function
[1]: https://i.stack.imgur.com/rCc7N.png
Right now it seems that strapi is a free variable for index() method. Due to lexical scope index() method has an access to free variables which were in a scope when the method is defined.
I do not know a structure of your project but as a solution I would recommend to explicitly pass a strapi object as a parameter in index method
module.exports = {
async index(ctx, strapi) {
let verification = {}
// Checks if there is a captcha provider
console.log('strapi' + strapi)
if (!(strapi.config.get('plugin.ezforms.captchaProvider.name') === 'none') && (strapi.config.get('plugin.ezforms.captchaProvider.name'))) {
verification = await strapi.plugin('ezforms').service(strapi.config.get('plugin.ezforms.captchaProvider.name')).validate(ctx.request.body.token)
//throws error if invalid
if (!verification.valid) {
...
Turns out you can pass the strapi object in the root of the module export.
module.exports = ({ strapi }) => ({
async index(ctx) {
...
Then in your test you can do
let result = await submitController({strapi}).index(ctx)
I'm making an user management with the Amazon Web Service names Cognito.
It runs smoothly on local but it is not when I use it on a Wamp server.
I can not figure out why... maybe cause to asynchronous execution ?
I'm using $q and .then(function() { ... }); to wait for it's execution.
This is how I do in my controller :
$scope.validateForm = function() {
if (AuthService.getActifUser() == false) {
//clear local storage
$storage.remove('userData');
}
//getting the form attributes
var datafirstName = {
Name: 'custom:first_name',
Value: $scope.firstName
};
var dataLastName = {
Name: 'custom:last_name',
Value: $scope.lastName
};
var dataEmail = {
Name: 'email',
Value: $scope.email
};
var attributeFirstName = AuthService.setAttributes(datafirstName);
var attributeLastName = AuthService.setAttributes(dataLastName);
var attributeEmail = AuthService.setAttributes(dataEmail);
var attributeList = [];
attributeList.push(attributeFirstName);
attributeList.push(attributeLastName);
attributeList.push(attributeEmail);
// signing try
AuthService.signin($scope.username, $scope.password, attributeList)
.then(function(res) {
// save username in local storage
$storage.set('userData', $scope.username);
// go to the verification page
routeService.goToView('/users-confirmation');
}, function(res) {
console.log(res);
);
}
And in the AuthService Factory :
AuthService.signin = function(username, password, attributeList) {
var deferred = $q.defer();
userPool.signUp(username, password, attributeList, null, function(err, result) {
if (err) {
alert(err);
deferred.reject('registering failled.');
}
console.log('successfully registered.');
deferred.resolve('successfully registered.');
});
return deferred.promise;
};
Unfortunatelly the routeService.goToView() method is never called.
Hope someone know why ?
Could you please add a handler for the promise rejection to ensure that the promise is not being rejected?
It could be that your promise is being rejected and the error hidden. Also, where is invalidPassword being defined?
My logout function, linked to a logout button is:
$scope.logoutUser = function() {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
ref.unauth();
console.log(ref.getAuth);
$state.transitionTo('login');
}
When I click logout, it prints this to the console:
function (){x("Firebase.getAuth",0,0,arguments.length);return this.k.P.we()}
I am checking for authData in my other controller with:
CONTROLLER:
.controller('SearchCtrl',
function ($scope, $http, Movie, $state, UsersRef, AuthData, $timeout) {
$scope.$on('$ionicView.enter', function () {
if (!AuthData) {
console.log("Auth data null!");
swal("Unauthorized", "You are not logged in", "error");
$state.transitionTo('login');
} else {
console.log("Auth data found: " + AuthData);
//do stuff
}
});
})
FACTORY:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
});
return data;
}
])
If I logout, then go back to the page linked to SearchCtrl by changing the URL, it still says it found the authData.
However, if I try and go to the search page the FIRST time I open the app, before anybody has logged in, it gives me the right error message and exits out to the login page.
How can I ensure the user can't go back into the app after logging out?
Welcome to async programming 101.
Firebase's onAuth methods listens for changes on auth state. When the auth state changes, the callback method you provide is invoked. But while it's waiting for auth state changes, your other code continues to run.
It most easy to see this if you add some log statements to your code:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
console.log('before onAuth');
ref.onAuth(function (authData) {
console.log('in callback');
if (authData) {
data = authData;
}
});
console.log('after onAuth');
return data;
}
])
The output is going to be:
before onAuth
after onAuth
in callback
Which is likely not what you expected when you wrote this code.
The simplest way to fix this in your code is to use the synchronous ref.getAuth() method:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
return ref.getAuth();
}
])
But you're going to run into this asynchronicity problem quite often. I highly recommend using and studying AngularFire instead of reinventing the wheel.
You are never cleaning data inside AuthData so it will always have data after the first guy logs in. I'm not familiar with Firebase but you need something like this in your AuthData factory:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
else
data = null;
});
return data;
}
])
I am trying to get an async intern test working using a separate module to do the request call. I am having an issue returning true once the test is done because I always get a timeout error, even though the request is successful, and the test runs to completion. After the test runs it just sits on the last page and times out. login_test.js is the test file, companyCreate is the request calling file that exists in an external module. I'm not quite sure what is happening to my test return call if I pass it into deferred.callback().
// login_test.js
define([
'intern!object',
'pages/loginpage',
'runtime/testConfig',
'intern/dojo/node!nconf',
'helpers/companyCreate',
'locators/loginpage',
'locators/companyselectionpage'
], function(registerSuite, LoginPage, conf, nconf, Company) {
var tests = {
name: 'Login test',
'Test': function() {
/* make a call to create a company
* param1: test function to run after we get response with login details
* param2: intern object so we can make it async
*/
Company.createCompany(function(response, testObj) {
testObj.timeout = 120000; //The default timeout is 30 seconds. Not enough
var region = nconf.get("region"); //Getting command line region value
var regionData = conf.get(region); //Fetching config data based on region
var loginId = regionData.LOGIN;
var password = regionData.PASSWORD;
var loginPage = new LoginPage(testObj.remote, regionData.DEFAULT_TIMEOUT);
var companySelectionPage = loginPage
.load(regionData.BASE_URL)
.loginIn(loginId, password);
var homePage = companySelectionPage
.doesCurrentURLContain('/companysel')
.isTitlePresent()
.selectCompany(CompanySelectionLocators.data);
return homePage
.doesCurrentURLContain('/homepage')
.getAccumulatedState();
}, this);
}
};
registerSuite(tests);
});
>
// companyCreate.js
define(function(require) {
var request = require('intern/dojo/request');
var Company = {
createCompany: function(callbackArg, testObj) {
// tell intern this is async
var deferred = testObj.async(120000);
// make post
request.post('https://internal.com/createcompany', {
query: {
version: ".0.1",
special: "true"
},
data: JSON.stringify({
userName: "Test",
password: "pass",
userEmail: "email#hi.com"
}),
headers: {
'Content-Type': "application/json"
}
}).then(function(response) {
// success, tell intern async is done, return test function to run and pass it the response
console.log(response);
return deferred.callback(callbackArg(response, testObj));
}, function(err) {
console.log(err);
}, function(evt) {
//console.log(evt);
});
}
};
return Company;
});
deferred.callback is intended to be used to wrap another callback that is executed at another time. It doesn’t resolve the underlying Promise, it returns a new function that, when invoked, resolves the Promise if the passed in callback function doesn’t throw an error. For example:
'Test': function () {
var dfd = this.async();
// this use of `dfd.callback`…
fs.readFile('foo.txt', dfd.callback(function (error, data) {
if (error) {
throw error;
}
assert.strictEqual(data, 'hello, world');
}));
// …is equivalent to this without it:
fs.readFile('foo.txt', function (error, data) {
if (error) {
dfd.reject(error);
return;
}
try {
assert.strictEqual(data, 'hello, world');
}
catch (error) {
dfd.reject(error);
return;
}
dfd.resolve();
}));
}
You should be using deferred.resolve, which resolves the promise to the value passed as the first argument. See the async tests documentation for more detailed information on each of these functions.
Working solution is below. I had no need for this.async. comapnyCreate.js returns the response and promise from the request. login_test.js runs the test after the promise is fulfilled. (still needs some error handling logic)
// login_test.js
define([
'intern!object',
'pages/loginpage',
'runtime/testConfig',
'intern/dojo/node!nconf',
'helpers/companyCreate',
'locators/loginpage',
'locators/companyselectionpage'
], function(registerSuite, LoginPage, conf, nconf, Company) {
var tests = {
name: 'Login test',
'Test': function() {
this.timeout = 60000;
var remote = this.remote;
return Company.createCompany().then(function(response) {
var region = nconf.get("region"); //Getting command line region value
var regionData = conf.get(region); //Fetching config data based on region
var loginId = regionData.LOGIN;
var password = regionData.PASSWORD;
var loginPage = new LoginPage(remote, regionData.DEFAULT_TIMEOUT);
var companySelectionPage = loginPage
.load(regionData.BASE_URL)
.loginIn(loginId, password);
var homePage = companySelectionPage
.doesCurrentURLContain('/companysel')
.isTitlePresent()
.selectCompany(CompanySelectionLocators.data);
return homePage
.doesCurrentURLContain('/homepage')
.getAccumulatedState();
});
}
};
registerSuite(tests);
});
// companyCreate.js
define(function(require) {
var request = require('intern/dojo/request');
var Company = {
createCompany: function() {
// make post
return request.post('https://internal.com/createcompany', {
query: {
version: ".0.1",
special: "true"
},
data: JSON.stringify({
userName: "Test",
password: "pass",
userEmail: "email#hi.com"
}),
headers: {
'Content-Type': "application/json"
}
});
}
};
return Company;
});
I'm adding a fresh, angular client-tier to a legacy app. Upon login the legacy up redirects to a 'home' url. The url contains a session id which I need to grab and use (in the url) for any subsequent gets/posts. After login I call:
browser.getCurrentUrl()
and then use a regex to extract the session id. I store the session id away and use it for later gets/posts.
The problem is though that browser.getCurrentUrl() returns a promise and all my tests run before I can get the session id back. How can I make protractor wait for the browser.getCurrentUrl() to resolve.
Specifically below where I have the code:
var sessionId = loginPage.login('testuser#example.com', 'testuser');
homePage = new HomePage(sessionId);
I really need all code to block on loginPage.login() so I'll have a defined session id. My home page tests and any other page tests will need the session id to run properly.
How can I achieve this in protractor?
Thanks!
The relevant parts of my code looks like this...
home.spec.js:
describe('home page tests', function() {
var loginPage = new LoginPage();
var homePage;
// get sessionId from login and create a new HomePage object from it
beforeEach(function() {
var sessionId = loginPage.login('testuser#example.com', 'testuser');
homePage = new HomePage(sessionId);
homePage.get();
});
describe('main elements of home page test', function() {
it('page has correct username as part of user menu', function() {
expect(homePage.getUsername()).toEqual('testuser#example.com');
});
});
});
login.po.js:
function LoginPage {
// ...snip...
this.login = function(username, password) {
return this.get()
.then(function() {
this.username.sendKeys(username);
this.password.sendKeys(password);
this.loginButton.click();
})
.then(function() {
return browser.getCurrentUrl().then(function(url) {
var groups = sessionIdRegex.exec(url);
// return the extracted session id or null if there is none
if (groups !== null) {
return sessionIdRegex.exec(url)[2];
} else {
return null;
}
});
});
};
}
home.po.js:
function HomePage(sessionId) {
this.username = element(by.binding('username'));
this.getUsername = function() {
return this.username.getText();
}
this.get = function() {
return browser.get(browser.baseUrl + sessionId + '#/home');
};
};
module.exports = HomePage;
The simplest could be to use expect:
Jasmine expectations are also adapted to understand promises. That's why the line
`expect(name.getText()).toEqual('Jane Doe');
works - this code actually adds an expectation task to the control flow, which will run after the other tasks.
login.po.js:
function LoginPage {
this.login = function(username, password) {
return this.get()
.then(function() {
this.username.sendKeys(username);
this.password.sendKeys(password);
this.loginButton.click();
})
.then(function() {
return browser.getCurrentUrl().then(function(url) {
var groups = sessionIdRegex.exec(url);
// return the extracted session id or null if there is none
if (groups !== null) {
return sessionIdRegex.exec(url)[2];
} else {
return null;
}
});
});
};
expect(this.login).not.toBeUndefined();
}