Sinon-stubbed REST call not shown as covered in tests - vue.js

I've got a method like this:
/**
* Get the list of available roles for a user.
* #returns {Promise}
* #param {String} token - the user token
*/
async getUserRoles(userToken){
const request = {
method: "get",
baseURL: this.baseURL + "/user_roles",
headers: this.auth_headers(userToken)
};
let response = await this.executeQuery(request);
return response.data;
}
Where executeQuery is simply:
async executeQuery(query) {
try {
return await axios(query);
}
catch(e){
return({data: {error: e}});
}
}
This is called by another part of the code (a Vue.js app), like this:
methods: {
async updatePublicProfile () {
this.data.userRoles = await restClient.getUserRoles(this.user().credentials.token);
...
It's stubbed in a unit test:
import Client from "#/lib/Client/RESTClient.js";
restStubRole = sinon.stub(Client.prototype, "getUserRoles");
restStubRole.returns([{some: 'data'}]);
await wrapper.vm.updatePublicProfile();
expect // etc. etc.
Mysteriously, getUserRoles is never shown as covered in coverage/lcov-report/index.html and is also not shown when tests are run on Github. Can anyone spot any suggestions as to why that might be?

Related

Chai Try Assert More Than Once Before Failing?

I'm doing a PUT request to an API, then I'm doing a GET request to see if what I PUT is there. The problem is that the API can take time between the PUT and GET requests for the data to propagate to be seen by the GET request. The testing framework I am using I just added waits, but I know this is not desirable, and I'm already seeing slow or failing tests as a result. To try to remove the waits I added the following, but I'm wondering if there is a cleaner way to do this?
// A function that simply tries x times for the checkingFunction to return a truthy value.
async function waitForCorrectApiResponse(checkingFunction, maxTries = 5, waitPeriod = 3000) {
let tries = 0;
while(maxTries > tries) {
const apiResponse = await checkingFunction();
if(apiResponse) {
return apiResponse;
}
// A wait method
await t.wait(waitPeriod);
tries ++;
}
}
const response = waitForCorrectApiResponse(async function(){
const apiResponse = await getApiResponse() // some random get API response function
if (apiResponse.body.hasOwnProperty('whatever') {
return apiResponse;
}
}
// Then do Chai assertions against response.....
For example off the top of my head I would like to be able to do something like the following...Basically, try a set of Chai assertions against a value x times, and each time if false reset the value.
async function waitForCorrectApiResponse(checkingFunction, maxTries = 5, waitPeriod = 3000) {
let tries = 0;
while(maxTries > tries) {
const apiResponse = await checkingFunction();
if(apiResponse) {
return apiResponse;
}
// A wait method
await t.wait(waitPeriod);
tries ++;
}
}
const response = waitForCorrectApiResponse(async function(){
const apiResponse = await getApiResponse() // some random get API response function
Chai.expect(apiResponse).to.have.property('whatever');
// Rest of Chai assertions.....
}
Update:
So I have been able to make the following, which I think is a little better but wondering if there is something even better?
async function retryChai(apiCall, assertions, maxTries = 5, waitPeriod = 3000) {
let tries = 0;
while (maxTries > tries) {
tries ++;
const apiResponse = await apiCall();
try {
const ok = await assertions(apiResponse);
return ok;
}
catch (e) {
if (maxTries === tries) {
assertions(apiResponse)
}
}
await t.wait(waitPeriod);
}
}
retryChai(
async function (){
// returns API response
},
function (response) {
// Put chai asserts here. i.e....
chai.expect(response.body.documents).to.be.empty;
}
)
As far as I understand, you are using the TestCafe Framework. TestCafe has a built-in assertion mechanism. So, it's not required to use the Chai library.
In general, your approach looks fine. It's difficult to say precisely how to improve your code since you shared just a small piece of code, not a working example.
In my opinion, your first approach is more practical since you split the API request and assertion into different functions.

How to get the ref(id) of my last instance created

I don't know how to simply get the ref(id) of my last instance created with faunadb.
I need to put it in an url.
I use this to create my instance:
/* code from functions/todos-create.js */
import faunadb from 'faunadb' /* Import faunaDB sdk */
/* configure faunaDB Client with our secret */
const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SECRET
})
/* export our lambda function as named "handler" export */
exports.handler = (event, context, callback) => {
/* parse the string body into a useable JS object */
const eventBody = JSON.stringify(event.body)
const data = JSON.parse(eventBody)
const mission = {
data: JSON.parse(data)
}
// {"title":"What I had for breakfast ..","completed":true}
/* construct the fauna query */
return client.query(q.Create(q.Ref("classes/missions"), mission))
.then((response) => {
console.log("success", response)
/* Success! return the response with statusCode 200 */
return callback(null, {
statusCode: 200,
body: JSON.stringify(response)
})
}).catch((error) => {
console.log("error", error)
/* Error! return the error with statusCode 400 */
return callback(null, {
statusCode: 400,
body: JSON.stringify(error)
})
})
}
I begin to dev, and I need to do this simply with faunadb, can you help me please ?
Instance creation returns a ref, as along with other instance metadata and the user supplied instance data. This means we can compose a select with the create to pull the data out that you require. Using the shell syntax:
Select("ref", Create(Class("missions"), {...})
will yield the ref. Of course if you'd like just the id part of the ref you can drill in further:
Select(["ref", "id"], Create(Class("missions"), {...})

Get item from AsyncStorage in React Native

I have a list of companies in React Native.
When I click on one of those companies I get the url of the API that is used for selected company. Then I store it to AsyncStorage and then I show the login screen. The function is as follows:
selectCompany(data_url, e) {
AsyncStorage.setItem("data_url", JSON.stringify(data_url), () => this.props.login());
}
Then on login page if I click on sign in button I go to the onLogin function, the function is as follows:
onLogin: function() {
fetch(data.url + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
},
And data.url comes from data.js file, and I try to get url from the data.js file as follows:
let data_url = AsyncStorage.getItem("data_url").then(json => JSON.parse(json));
module.exports = {
url: data_url,
.....
}
But it doesn't work. Any advice?
AsyncStorage is async, therefore data_url will not be defined until it's retrieved what its looking for, you would need to move the fetch into the promise thats returned from the get so it will run it once it's done getting the data. This might be one way you tackle it:
const data_url = () => AsyncStorage.getItem("data_url"); //change this into a function
module.exports = {
url: data_url,
.....
}
now inside your component...
onLogin: function() {
data.url().then((url) => {
fetch(JSON.parse(url) + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
});
},
AsyncStorage.getItem is a promise and needs to await for response rather than accessing direct and the function calling it should be defined as async. Here is an example to retrieve from AsyncStorage..
export async function getAccessKey(){
let accessToken = await AsyncStorage.getItem(ACCESS_TOKEN);
return accessToken;
}

react-native-fbsdk post message facebook Graph API FBGraphRequest

I went through FBSDK Sharing documentation here, but I can't find a simple example where one can just post a simple message to timeline (not a link, not a photo, not a video) using FBShareDialog.
I know I can do it running a web request which essentially does this:
POST graph.facebook.com/me/feed?
message="My new post message!"&
access_token={your-access-token}
as described in Graph API docs, but again - I want to use ShareDialog to have consistent UI.
How do I do it? Thank you.
Note: All user lower case "post" refers to the act of posting to a users wall. All upper case "POST" refers to HTTP request method.
Facebooks offical react native SDK is located here https://developers.facebook.com/docs/react-native/
Note there are three different component:
Login
Sharing
Graph API.
The first two are self explanatory and the examples are provided on the site.
The Graph API is the primary way to get data in and out of Facebook's
social graph. It's a low-level HTTP-based API that is used to query
data, post new stories, upload photos and a variety of other tasks
that an app might need to do.
Facebook Graph API is just a REST API which lets you interact with the fb data via HTTP methods( GET, POST, DELETE etc). react-native-fbsdk just layer on top of it which makes it easier to make these request.
There are two prerequisites to posting to a user time.
Ensuring your fb app is correctly setup: https://developers.facebook.com/apps/
Obtaining a user access token with publish_actions permission can be used to publish new posts.
Once you have obtained these you can post a message using the react native GRAPH API.
But first lets have a look at how you would do this simply using HTTP rather then the RN-SDK:
https://developers.facebook.com/docs/graph-api/reference/v2.7/user/feed
POST /v2.7/me/feed HTTP/1.1
Host: graph.facebook.com
message=This+is+a+test+message
According to this we need to make a POST request to the location /v2.7/me/feed with the message defined as a paramater.
To finally answer your question how to we post to the users timeline using the react native sdk (). Lets have a look at the rnsdk docs: https://developers.facebook.com/docs/react-native/graph-api
It seems like we need two objects GraphRequest (to create a request) and GraphRequestManager (to send the request)
const FBSDK = require('react-native-fbsdk');
const {
FBGraphRequest,
FBGraphRequestManager,
} = FBSDK;
Since there is no example provided on how to post to the user wall using these two objects we need to look into the source code:
https://github.com/facebook/react-native-fbsdk/blob/master/js/FBGraphRequest.js
We can see from the constructor it takes three parameters:
/**
* Constructs a new Graph API request.
*/
constructor(
graphPath: string,
config: ?GraphRequestConfig,
callback: ?GraphRequestCallback,
)
We know the graphPath = "/me/feed" from the Graph API docs. The callback will just be a function called upon return of the request. This leaves us with the config object, which is defined in the source as:
type GraphRequestConfig = {
/**
* The httpMethod to use for the request, for example "GET" or "POST".
*/
httpMethod?: string,
/**
* The Graph API version to use (e.g., "v2.0")
*/
version?: string,
/**
* The request parameters.
*/
parameters?: GraphRequestParameters,
/**
* The access token used by the request.
*/
accessToken?: string
};
So our config object will look something like this:
const postRequestParams = {
fields: {
message: 'Hello World!'
}
}
const postRequestConfig = {
httpMethod: 'POST',
version: 'v2.7',
parameters: postRequestParams,
accessToken: token.toString() //pre-obtained access token
}
Putting it altogether:
const FBSDK = require('react-native-fbsdk');
const {
FBGraphRequest,
FBGraphRequestManager,
} = FBSDK;
_responseInfoCallback(error: ?Object, result: ?Object) {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
alert('Success fetching data: ' + result.toString());
}
}
const postRequestParams = {
fields: {
message: 'Hello World!'
}
}
const postRequestConfig = {
httpMethod: 'POST',
version: 'v2.7',
parameters: postRequestParams,
accessToken: token.toString()
}
const infoRequest = new GraphRequest(
'/me/feed',
postRequestConfig,
this._responseInfoCallback,
);
new FBGraphRequestManager().addRequest(infoRequest).start();
I posted to facebook with react native 0.43 by above code but i changed on postRequestParams
const postRequestParams = {
message: {
string: 'Hello World!'
}
}
Here is all of me.
const FBSDK = require('react-native-fbsdk');
const {
GraphRequest,
GraphRequestManager,
AccessToken
} = FBSDK;
class PostScreen extends React.Component {
postToFacebook = () => {
AccessToken.getCurrentAccessToken().then(
(data) => {
let tempAccesstoken = data.accessToken;
const _responseInfoCallback = (error, result) => {
console.log(result);
}
const postRequestParams = {
message: {
string: "Hello world!"
}
}
const postRequestConfig = {
httpMethod: "POST",
version: "v2.9",
parameters: postRequestParams,
accessToken: tempAccesstoken
}
console.log(postRequestConfig);
const infoRequest = new GraphRequest(
"/me/feed",
postRequestConfig,
_responseInfoCallback,
);
console.log("infoRequest");
console.log(infoRequest);
new GraphRequestManager().addRequest(infoRequest).start();
});
}
}

How to broadcast to other controllers when load with module.config or .run in Angularjs

I have a checking when reading the web page,then using the result to refresh sidebar by ng-repeat,but I have errors :
Uncaught Error: Unknown provider: $scope from myModule or
Uncaught Error: Unknown provider: $scope from sharedService
How can I resolve it?
Here is my code
module:
var myModule = angular.module('myModule', []);
service for broadcast:
myModule.factory('mySharedService', function($rootScope) { //service
var sharedService = {};
sharedService.keyHistory = [];
sharedService.linkHistory = [];
sharedService.prepForBroadcast = function(key,link) {
this.keyHistory = key;
this.linkHistory = link;
this.broadcastItem();
};
sharedService.prepForBroadcastAdd =function(key){
console.log(this.keyHistory.push(key));
//this.linkHistory = linkHistory+link;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
config to do Checking:
myModule.config(function($scope,sharedService){
$.ajax({
url:"/fly/AJAX",
type:"POST",
contentType:'application/x-www-form-urlencoded; charset=UTF-8',
datatype:"json",
success:function(data){
if(data!=null){
var loginResult = $.parseJSON(data);
if (loginResult.success == true){
console.log("login success");
$("#userLable").html(loginResult.userName+'('+loginResult.loginID+')');//
if (loginResult.hasHistory==true) {
sharedService.prepForBroadcast(loginResult.searchHistory,[]);
console.log("broadcast");
}
};
}
}
});
});
SideCtrl:
function SideCtrl($scope,sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.keyHistory =sharedService.keyHistory;
$scope.linkHistory = sharedService.linkHistory;
});
}
SideCtrl.$inject = ['$scope', 'mySharedService'];
THX !
The error is due to trying to request a $scope in a config block, which you can't do. If I understand what you're trying to do, then I also think you're over-complicating it. I'd solve the problem a little differently. The details would depend on your requirements and use case, but based on the information you gave...
I'd have a service responsible for communication with the server and storing the state:
app.factory( 'loginService', function ( $http ) {
var result;
function doRequest( data ) {
// just flesh out this post request to suit your needs...
return $http.post( '/fly/ajax', data, {} )
.then( function ( response ) {
// assuming you don't care about the headers, etc.
return response.data;
});
}
// Do it once initially
if ( ! angular.isDefined( result ) ) {
result = doRequest();
}
// return the service's public API
return {
getStatus: function () { return result; },
login: doRequest
};
});
Now the first time this service is requested, the $http request will be made. If you're accessing this from multiple controllers, the post will only occur once because of the isDefined statement. You can then use this in your controllers:
app.controller( 'MainCtrl', function( $scope, loginService ) {
loginService.getStatus().then( function ( data ) {
// do whatever you need to with your data.
// it is only guaranteed to exist as of now, because $http returns a promise
});
});
Every controller accesses it the same way, but it was still only called once! You can set values against the scope and access it from your views, if you want:
app.controller( 'MainCtrl', function( $scope, loginService ) {
loginService.getStatus().then( function ( data ) {
$scope.loginId = data.loginID;
});
});
And in your view:
<h1>Welcome, {{loginId || 'guest'}}!</h1>
And if you need to, you call the function again:
app.controller( 'MainCtrl', function( $scope, loginService ) {
// ...
loginService.login( $scope.user ).then( function ( data ) {
$scope.loginId = data.loginID;
});
// ...
});
As you can see, broadcasting an event is totally unnecessary.
I would do it differently. I would create some sort of more top-level controller, like function MainController($rootScope, $scope, sharedService) and wire it up with body: <body ng-controller='mainController' ng-init='init()'. After that you should create init() method in MainController.
Inside this initialization method I would call sharedService which should make AJAX request (via $http! that's the best practice, and it's very similar to jQuery) and broadcast proper event when required.
That way you make sure to call initialization just once (when MainController is initializing), you stick to the angular's best practices and avoid dodgy looking code.