I have a test case where my code make request to server and check if the filename with specified date exists. Since GET request is async call, how can I make sure that I have the filename from the server before I check if it is a specified date?
Here's excerpt of my code :
var re = new RInterface();
it('data show exists', function() {
target.each(function(ele){
browser.actions().mouseMove(ele).perform();
re.get(function(result){
expect(result).toEqual(true);
});
});
});
RInterface.js
var Service = function() {
var serv = this;
var uname = atob(settings.username);
var pwd = atob(settings.password);
var url = 'https://' + uname + ':' + pwd + '#' + settings.Url + '/' + settings.format + '/' + settings.period;
var completeURL = url;
var today = DateString();
serv.get = function(callback) {
var dataStrAry = [];
var count = 0;
request(completeURL, function (error, response, body) {
if (!error && response.statusCode == 200) {
var serverData = JSON.parse(body);
var split = serverData[serverData.length-1].Name.split(" ");
var target = split[split.length-1].split(".")[0];
// Check if the file with current date is available
// If it is, then assume data is saved on the server
if(target == today) {
console.log("equal");
callback(true);
}
else {
console.log("not equal");
callback(false);
}
}
else {
console.log("errror call");
callback(false);
return;
}
});
};
So, re.get is where I make GET request to the server and I passed callback function to be called at the end of get request. The problem, I think is protractor complete executing the test before my code gets data from the server. How do I force protractor to wait so that I can check the returned data? My current workaround is put the get request inside beforeEach and seems that protractor forces test to wait for it finish executing.
You need to handle the result of your request with a Promise if you want the control flow to wait for it:
var re = new RInterface();
it('data show exists', function() {
target.each(function(ele){
browser.actions().mouseMove(ele).perform();
expect(re.get()).toEqual(true);
});
});
var Service = function() {
...
this.get = function() {
var defer = protractor.promise.defer();
...
request(completeURL, function (error, response, body) {
if (!error && response.statusCode == 200) {
var result = ...
defer.fulfill(result);
} else {
defer.reject(error);
}
});
return defer.promise;
};
};
You can make it easier by using browser.wait, just wrap the request function (which returns a promise) like
browser.wait(request...).then(result => {
// here continue execution with the result of the request
}
This will make the browser wait for your promise.
Related
I wrote a helper methods to add a network response listener over Puppeteer page instance. the code looks like this
let Helper = codecept_helper;
class CheckHelper extends Helper {
async listenRequest(listener)
{
const helper = this.helpers['Puppeteer'];
await helper.page.setRequestInterception(true);
helper.page.on("request",listener);
return helper._waitForAction();
}
async listenResponse(listener)
{
const helper = this.helpers['Puppeteer'];
helper.page.on("response",listener);
return helper._waitForAction();
}
}
module.exports = CheckHelper;
then in the test script
let self=this;
I.listenResponse((response)=>{
if(response.url().match(/github.*\.js/) && response.headers()['content-length']>1000) {
//codeceptjs.event.emit(codeceptjs.event.test.failed, self, 'js file is too big!');
//codeceptjs.recorder.throw('js file is too big!')
//codeceptjs.recorder.stop();
//throw new Error('js file is too big!')
}
})
I.amOnPage("https://www.github.com");
i first add response listener, then i goto "github", when some js file size is too big,i will throw out an error,in order too check content size is correctly.
however, even i throw error out (like the comments codes did), the main test flow just not stop, how do i do is the right way?
well,i found a solution later
i recorded all the page response into a custom object in the page instance.
later i wrote a help methods to check whole records.
//in helper.js
startRecordResponse() {
const helper = this.helpers['Puppeteer'];
helper.page.on("response", (res) => {
//record all response instance into savedResponse object inside page, we'll use it later
helper.page.savedResponse = helper.page.savedResponse || {};
helper.page.savedResponse[res.url()] = res;
});
return helper._waitForAction();
}
checkFileIsTooBig(filter, sizeLimit) {
const helper = this.helpers['Puppeteer'];
//use the data recorded in savedResponse object
Object.keys(helper.page.savedResponse).forEach((url) => {
var res = helper.page.savedResponse[url];
if (((filter instanceof RegExp && filter.test(url)) || (typeof filter == "string" && url.indexOf(filter) != -1)) && res.headers()['content-length'] > sizeLimit) {
throw new Error(`file ${url} is too big,${res.headers()['content-length']} > ${sizeLimit}`)
}
})
return helper._waitForAction();
}
then in test file
Before((I) => {
I.startRecordResponse();
I.amOnPage("https://www.github.com");
});
Scenario('github_test', (I) => {
//check a js file contain github is less than 100 bytes
I.checkFileIsTooBig(/github.*\.js/,100);
}
);
I'm trying to send a file with a post with ionic 2
In order to ask for the file, i use an invisible input type file
<input type="file" accept="image/*;" #file id="fileUpoload" style="display: none">
The button call the function in this way:
(click)="onFileUpoloadButtonPressed(file)"
And this is the function called:
onFileUpoloadButtonPressed(element){
document.getElementById("fileUpoload").onchange = function(e : any){
let file = {
name: e.srcElement.files[0].name,
file: e.srcElement.files[0],
};
//I get the id of the user since i have to perform an edit call to my api
this.storage.get("userLogged").then((value) => {
setTimeout(function(){
this.postChangeAvatar(this, parseInt(value.data.utenti_id), file,
function (ext, result){ //Success callback
console.log(result);
},
function(ext, error){ //Error callback
console.log(error);
alert(error);
}
)
}, 100)
})
}
element.click();
}
This is the postChangeAvatar function that perform the post request:
postChangeAvatar(ext, id, file, successCallback, errorCallback){
let formData : any = new FormData();
let xhr : any = new XMLHttpRequest();
console.log(id);
console.log(file); //File is successfully get
formData.append('user_photo', file.file, file.name);
for (var pair of formData.entries()) { //This is showing nothing
console.log(pair[0]+ ', ' + pair[1]);
}
xhr.onreadystatechange = () => {
if (xhr.readyState == 4){
if (xhr.status == 200){
successCallback(ext, xhr.response);
}
else {
errorCallback(ext, xhr.response);
}
}
}
xhr.open('POST', "http://xxxxxxxxxx/api/edit/utenti/" + id, true);
xhr.send(formData);
}
The post is performed but the formData remains empty after append the file, trying to print the formdata with the for each doesn't show anything, so the only thing wrong is the formData being empty when post is performed
As you can see i tried to encapsulate the entire request in a setTimeout to be sure the file is obtained, the file is in there but is not appendend in the formData
From the server i can see the body of the request empty
I tried this method in another project and in there was successfully working so i'm a bit surprised seeing this not working
If i'm not able to get this working maybe there's another way to post selected files with ionic 2?
Here is working piece of code (base64 file upload). Try setting header. Add enctype to Access-Control-Expose-Headers to prevent CORS.
insertPost(data): Observable<any> {
let headers = new Headers({ "enctype": "multipart/form-data" });
data.userId = this.globalProvider.userId;
var form_data = new FormData();
for (var key in data) {
form_data.append(key, data[key]);
}
return this.http.post(`${baseURL}insertPost`, form_data, { headers: headers })
.map((response: Response) => {
return response.json();
})
.catch(this.handleError);
}
I've been trying to get my refresh token to work for a while now, and I hope I'm close. My token refreshes and triggers a subsequent 200 call to whatever call caused the 401, but my the data on my page doesn't refresh.
When an access token expires, the following happens:
After the 401, the GetListofCompanyNames returns 200 with a list of names using the correct updated access token. However, my dropdown does not refresh.
My interceptor:
app.factory('authInterceptorService',['$q', '$location', 'localStorageService', '$injector', function($q, $location, localStorageService, $injector) {
return {
request: function(config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
},
responseError: function(rejection) {
//var promise = $q.reject(rejection);
var authService = $injector.get('authService');
if (rejection.status === 401) {
// refresh the token
authService.refreshToken().then(function() {
// retry the request
var $http = $injector.get('$http');
return $http(rejection.config);
});
}
if (rejection.status === 400) {
authService.logOut();
$location.path('/login');
}
return $q.reject(rejection);
}
};
}
]);
My return statement on the 401 rejection looks suspect here, but I'm not sure what to replace it with. Thereby my question is: How can I get my page to refresh it's data when I make the new call?
Update:
This gets me past when the 200 returns and I can get a dropdown to refresh, but I lose any state on the page (ex. selected dropdown) with the below.
authService.refreshToken().then(function() {
var $state = $injector.get('$state');
$state.reload();
});
Back to the drawing board!
Try putting up your retry call in $timeout, it should work.
Here's the updated code:
app.factory('authInterceptorService',['$q', '$location', 'localStorageService', '$injector', function($q, $location, localStorageService, $injector) {
return {
request: function(config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
},
responseError: function(rejection) {
//var promise = $q.reject(rejection);
var authService = $injector.get('authService');
if (rejection.status === 401) {
// refresh the token
authService.refreshToken().then(function() {
// retry the request
return $timeout(function() {
var $http = $injector.get('$http');
return $http(rejection.config);
}});
}
if (rejection.status === 400) {
authService.logOut();
$location.path('/login');
}
return $q.reject(rejection);
}
};
}
]);
$timeout returns a promise that is completed with what is returned
from the function parameter, so we can conveniently just return the
$http call wrapped in $timeout.
Thanks.
I think you may want to change up how you go about this. One way to go about this would be to inject the $rootScope into your authInterceptorService and then once you successfully refresh the token, call something like $rootScope.broadcast('tokenRefreshed').
I don't quite know how you have set up the view and controller that handles your dropdown, but I would set up a listener for that 'tokenRefreshed' event. From here, you can do another call to GetListofCompanyNames. If you do it this way you can easily control and ensure that the model gets updated.
My final solution:
app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', '$injector', function($q, $location, localStorageService, $injector) {
var $http;
var retryHttpRequest = function(config, deferred) {
$http = $http || $injector.get('$http');
$http(config).then(function(response) {
deferred.resolve(response);
},
function(response) {
deferred.reject(response);
});
}
return {
request: function(config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
},
responseError: function(rejection) {
var deferred = $q.defer();
if (rejection.status === 401) {
var authService = $injector.get('authService');
authService.refreshToken().then(function() {
retryHttpRequest(rejection.config, deferred);
},
function () {
authService.logOut();
$location.path('/login');
deferred.reject(rejection);
});
} else {
deferred.reject(rejection);
}
return deferred.promise;
}
};
}
]);
Copied almost 1 for 1 from https://github.com/tjoudeh/AngularJSAuthentication/blob/master/AngularJSAuthentication.Web/app/services/authInterceptorService.js .
This one transparently handles all requests and refreshes them when necessary. It logs out users when the refresh token is expired and passes errors along to the controllers by properly rejecting them. However, it doesn't seem to work with multiple in flight requests, I'll look into that when I get a use case for it in my system.
I'm attempting to build a simple Alexa skill to return data from an API using the [Node.js ASK] (https://developer.amazon.com/public/community/post/Tx213D2XQIYH864/Announcing-the-Alexa-Skills-Kit-for-Node-js). I have put the http get within a handler, but Alexa completes the handler before the callback asynchronously returns the API data.
I have been searching for answers, and my thoughts are currently:
not use node.js
figure out a way to synchronously get the data
Something simple I am missing
Core of the code:
exports.handler = function(event, context, callback) {
var alexa = Alexa.handler(event, context);
alexa.registerHandlers(handler);
alexa.execute();
};
var handler = Alexa.CreateStateHandler(states.x, {
'intent': function() {
var options = {
host: baseURL,
path: pathURL
};
callback = function(response) {
var str = "";
response.on('data', function(piece) {
str += piece;
});
response.on('end', function() {
//does not get executed
this.emit(':tell', str, "test");
});
}
http.request(options, callback).end();
//this does get executed if I leave this here
this.emit(':tell'...);
};
I think you are having a scope issue.
try ...
response.on('end',() => {
this.emit(':tell', str, "test");
});
I'm using Meteor for first time and i'm trying to have a simple http call within a method so i can call this method from the client.
The problem is that this async call it's keep running even if i put it within a wrapper.
Client side:
Meteor.call('getToken', function(error, results) {
console.log('entered');
if(error) {
console.log(error);
} else {
console.log(results);
}
});
Server Side
Meteor.methods({
getToken: function(){
// App url
var appUrl = 'myAppUrl';
// Key credentials
var apiKey = 'mykey';
var apiSecret = 'mySecret';
function asyncCall(){
Meteor.http.call(
'POST',
appUrl,
{
data: {
key: apiKey,
secret: apiSecret
}
}, function (err, res) {
if(err){
return err;
} else {
return res;
}
}
);
}
var syncCall = Meteor.wrapAsync(asyncCall);
// now you can return the result to client.
return syncCall;
}
});
I'm always getting an undefined return.
If i log the response within the http.post call i'm geting the correct response.
If i try to log the syncCall i get nothing.
I would very appreciate any help on this.
You should use the synchronous version of HTTP.post in this case. Give something like this a try:
Meteor.methods({
getToken: function() {
var appUrl = 'myAppUrl';
var data = {apiKey: 'mykey', apiSecret: 'mySecret'};
try {
var result = HTTP.post(appUrl, {data: data});
return result;
} catch (err) {
return err;
}
}
});
Instead of returning the err I'd recommend determining what kind of error was thrown and then just throw new Meteor.Error(...) so the client can see the error as its first callback argument.