Unit test for creation object - javascript

I'm started to learn unit test using jasmine. And maybe someone can explain me - how I need to check creating new object like:
const outLog = {
time: newTime,
request: {
direction: 'OUT',
method: data.request.method,
title: data.request.url,
body: data.request.data,
headers: getHeaders(data.request.headers),
},
response: {
status: '',
headers: {},
body: {},
},
};
if (data.response && data.response !== null) {
outLog.response.status = data.response.status;
outLog.response.body = data.response.data;
outLog.response.headers = getHeaders(data.response.headers);
}
log.out.push(outLog);
Do I need to fill the fields like request method, request title with real data and check
const outLog = {
request: {
method: 'POST',
title: 'title',
...
...
},
}
expect(outLog.request.method).toEqual('POST');
expect(outLog.request.title).toEqual('title');
Thanks!

For checking objects, most frameworks usually provide some sort of pretty diff when you compare two objects.
To get this you'd first need to compare it to an object literal:
expect(outLog).toEqual({
request: {
method: 'POST',
title: 'title',
...
})
This also removes a LOT of logic from all the expect statements, which should help with maintenance and debugging. Additionally, when there are many different assertions, the earlier ones mask the later ones, creating unnecessary feedback loops, when the same thing could be accomplished with a single assertion. one of the unit testing thought leaders addresses this by recommending that unit tests should only ever have a single assertion to help keep them focused, reduce debugging time, and make them easier to work on and understand.

Related

Http PUT deletes data that hasn't been changed

I am trying to edit a JSON data base of shifts. I am writing in Javascript using react.
This is my understanding of the PUT syntax:
const editShift = async (changed, id) => {
const res = await fetch(`http://localhost:5000/shifts/${id}`, {
method: 'PUT',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(changed)
})
const data = await res.json()
setShifts([...shifts, data])
}
data.json:
{
"shifts": [
{
"title": "test",
"startDate": "2018-06-25T07:30:00.000Z",
"endDate": "2018-06-25T08:00:00.000Z",
"allDay": false,
"id": 1
},
{
"title": "test2",
"startDate": "2018-06-28T07:30:00.000Z",
"endDate": "2018-06-28T08:00:00.000Z",
"allDay": false,
"id": 2
}
]
}
The result is that the new shift will hold only the fields that have been changed and delete the rest. Any ideas why?
To get previous shifts you should use a callback inside setShifts like:
setShifts(prevShifts => ([...prevShifts, data]))
The PUT request should work like that.
A PUT request should PUT the data into place of the old record.
You would want to use a PATCH request for only PATCHing data upon the old record.
That set aside, it depends on your /shifts endpoint on how this is actually handled.
I had to send the whole updated shift into the changed field, with the fields that were not changed. I don't know why but it works so okay.
For a question like this, it'd be helpful if you posted before and after examples of data
database empty
request #1, payload equals X
request #2, payload equals Y
Also, knowing what your Server handler is doing would help determine what's going on. I have a feeling what's happening is something like this
// API handler
(req) => {
const dbData = getDBData();
dbData.shifts = req.shifts; // overwrite old with new
}
When what you need is something like
(req) => {
const dbData = getDBData();
req.shifts.forEach((newShiftData) => {
const shiftId = newShiftData.id;
if (dbData[shiftId]) {
dbData[shiftId] = { ...dbData[shiftId], ...newShiftData }; // merge old with new
}
});
// - check if data changed
// - update DB
}
PUT requests replace all the data at the target URI. They don't automatically merge changes.
It's not clear if you are the author of this API or just a consumer, but if you are consuming the API you should either look for a different API that lets you only send edits (HTTP method might be POST or PATCH), or you need to send the entire list of shifts.
Alternatively it might also be possible that there's a specific endpoint for each shift, so you can send exactly 1 PUT request to a shift endpoint just to edit that specific endpoint.

Duplicate keys in a REST connector query in loopback

I would like to ask if you know how could I duplicate parameters in a loopback REST connector query.
I have the following code:
details: {
'template': {
'method': 'GET',
'debug': true,
'url': 'https://www.example.com/data',
'timeout': 10000,
'headers': {
'Authorization': 'Bearer {token}'
},
'query': {
q: 'PHOTOS'
q: 'DETAILS',
id: '{id}'
},
'options': {
'useQuerystring': true
},
'responsePath': '$'
},
'functions': {
'searchData': [
'token',
'id'
]
}
}
The problem for that it is that it seems that loopback override the value of the parameter q by the last one, because I get only information for the last parameter.
Any idea how to solve it?
Thank you in avance.
You just have to pass them as an array:
'query': {
q: ['PHOTOS', 'DETAILS'],
id: '{id}'
},
Note that the options key, is passed to request and here's the documentation for useQuerystring:
useQuerystring - If true, use querystring to stringify and parse querystrings, otherwise use qs (default: false). Set this option
to true if you need arrays to be serialized as foo=bar&foo=baz
instead of the default foo[0]=bar&foo[1]=baz.
So if you remove it you'll end with something like ?q[0]=PHOTOS&q[1]=DETAILS.
You can also another option there:
qsStringifyOptions - object containing options to pass to the qs.stringify method.
Alternatively pass options to the
querystring.stringify
method using this format {sep:';', eq:':', options:{}}. For example,
to change the way arrays are converted to query strings using the qs
module pass the arrayFormat option with one of
indices|brackets|repeat
So you can actually end up with the same thing adding this:
"options": {
"qsStringifyOptions": {
"arrayFormat": "repeat"
}
}
And if you want to have just the brackets(something like this ?q[]=PHOTOS&q[]=DETAILS) you can specify brackets option:
"options": {
"qsStringifyOptions": {
"arrayFormat": "brackets"
}
}

Malformed request when creating billing plan

So I a using the node paypal-rest-sdk module and I'm trying to create a billing plan. Using the documentation here I made this JSON:
const billingPlanAttributes = {
name: 'Subscription',
description: 'Monthly subscription plan',
type: 'INFINITE',
payment_definitions: [{
name: 'Regular monthly infinite payments',
type: 'REGULAR',
frequency_interval: '1',
frequency: 'MONTH',
cycles: '0',
amount: {
currency: 'USD',
amount: '4.99',
},
}],
merchant_preferences: {
cancel_url: 'http://localhost:3000/subscribe/cancel',
return_url: 'http://localhost:3000/subscribe/return',
auto_bill_amount: 'YES',
},
};
But when using the paypal.billingPlan.create(... function I get the error 'MALFORMED_REQUEST', 'Incoming JSON request does not map to API request'. So I guess my JSON is not in the correct format or I'm missing something that is need.
The documentation has a charge_models key but it does not mention that it is required unlike other keys.
If you can point me in the right direction that would be great.
Edit: changed the return url and cancel url to include the full domain but still same error.
There could be more to this, but I noticed one thing wrong with your JSON. Remove commas for items last in a list. After 'amount' and 'merchant_preferences'. JSON is picky.
late answer, I know, but ran in exactly the same issue than you.
In the create function of the billing plan
public function create($apiContext = null, $restCall = null)
{
$payLoad = $this->toJSON();
$json = self::executeCall(
"/v1/payments/billing-plans/",
"POST",
$payLoad,
null,
$apiContext,
$restCall
);
$this->fromJson($json);
return $this;
}
I found out, that the toJSON method always returned false. Why the hell!?!
I did the same as you did and copied the complete sample code into my code. Then it worked as expected. Now I checked, what the difference was in to my code.
I realized, that I used an umlauts (ä,ü,ö) in the name and description of the billing plan. I changed the umlauts to
'ä' => 'ae',
'ö' => 'oe'
'ü' => 'ue'
Then it worked fine! Maybe someone else is running in this issue, too.

Creating an envelope from a template returning "UNSPECIFIED_ERROR"

When I try to create an envelope from a template I get a response of:
{ errorCode: 'UNSPECIFIED_ERROR',
message: 'Non-static method requires a target.' }
Here's what I'm doing so far:
First I login, which returns
{ loginAccounts:
[ { name: '*****',
accountId: '*****',
baseUrl: 'https://demo.docusign.net/restapi/v2/accounts/******',
isDefault: 'true',
userName: '***** ********',
userId: '*******-*****-*****-*****-*********',
email: '********#*******.com',
siteDescription: '' } ] }
So then I take the baseUrl out of that response and I attempt to create the envelope. I'm using the hapi framework and async.waterfall of the async library, so for anyone unfamiliar with either of these my use of the async library uses the next callback to call the next function which in this case would be to get the url for the iframe, and with our usage of the hapi framework AppServer.Wreck is roughy equivalent to request:
function prepareEnvelope(baseUrl, next) {
var createEntitlementTemplateId = "99C44F50-2C97-4074-896B-2454969CAEF7";
var getEnvelopeUrl = baseUrl + "/envelopes";
var options = {
headers: {
"X-DocuSign-Authentication": JSON.stringify(authHeader),
"Content-Type": "application/json",
"Accept": "application/json",
"Content-Disposition": "form-data"
},
body : JSON.stringify({
status: "sent",
emailSubject: "Test email subject",
emailBlurb: "My email blurb",
templateId: createEntitlementTemplateId,
templateRoles: [
{
email: "anemailaddress#gmail.com",
name: "Recipient Name",
roleName: "Signer1",
clientUserId: "1099", // TODO: replace with the user's id
tabs : {
textTabs : [
{
tabLabel : "acct_nmbr",
value : "123456"
},
{
tabLabel : "hm_phn_nmbr",
value : "8005882300"
},
{
tabLabel : "nm",
value : "Mr Foo Bar"
}
]
}
}
]
})
};
console.log("--------> options: ", options); // REMOVE THIS ====
AppServer.Wreck.post(getEnvelopeUrl, options, function(err, res, body) {
console.log("Request Envelope Result: \r\n", JSON.parse(body));
next(null, body, baseUrl);
});
}
And what I get back is:
{ errorCode: 'UNSPECIFIED_ERROR',
message: 'Non-static method requires a target.' }
From a little googling it look like 'Non-static method requires a target.' is a C# error and doesn't really give me much indication of what part of my configuration object is wrong.
I've tried a simpler version of this call stripping out all of the tabs and clientUserId and I get the same response.
I created my template on the Docusign website and I haven't ruled out that something is set up incorrectly there. I created a template, confirmed that Docusign noticed the named form fields, and created a 'placeholder' templateRole.
Here's the templateRole placeholder:
Here's one of the named fields that I want to populate and corresponding data label:
As a side note, I was able to get the basic vanilla example working without named fields nor using a template using the docusign node package just fine but I didn't see any way to use tabs with named form fields with the library and decided that I'd rather have more fine-grained control over what I'm doing anyway and so I opted for just hitting the APIs.
Surprisingly when I search SO for the errorCode and message I'm getting I could only find one post without a resolution :/
Of course any help will be greatly appreciated. Please don't hesitate to let me know if you need any additional information.
Once I received feedback from Docusign that my api call had an empty body it didn't take but a couple minutes for me to realize that the issue was my options object containing a body property rather than a payload property, as is done in the hapi framework.

Nested attributes with Angular.js

I have been racking my brain and google all morning trying to figure this out but I have come to the conclusion that I need to ask the experts! I am trying to do nested attributes with Sinatra and Angular, don't worry about the Sinatra side of things I am just trying to get the data to the server side in the correct manner at the moment. Please see the code below for an explanation of
My Input:
<input type="text" placeholder="{{item.placeholder}}" ng-model="question.possible_answer_attributes[$index][title]" class="form-control" />
My model object:
$scope.question = new Question({
poll_id: parseInt($routeParams.id),
title: '',
kind: 'open',
possible_answer_attributes: [] // I believe the issue may lie here
});
My factory:
.factory('Question', function($resource) {
return $resource('/api/questions/:id', { id: '#id' }, {
'update' : { method: 'PUT' },
'get_by_poll' : { method: 'GET', url: '/api/questions_by_poll/:id', isArray: true }
});
})
My object at time of running save function:
{"poll_id"=>1, "title"=>"123123", "kind"=>"multiple", "possible_answer_attributes"=>[{"undefined"=>"412312"}, {"undefined"=>"1234124"}, {"undefined"=>"234235234"}]}
I do not understand why my "possible_answer_attributes" keys are coming through as "undefined". It may be something very simple that I have missed, but any feedback would be great!
Thanks in advance!
In order to address the title property, you would need to use a string to index into the object:
ng-model="question.possible_answer_attributes[$index]['title']"
This should hold as long as possible_answer_attributes array looks like:
[{ title: 'my title' }]

Categories

Resources